Bug 969375 - MIPS port: Added BaselineCompiler code. r=jandem,froydnj
authorBranislav Rankov <branislav.rankov@imgtec.com>
Fri, 14 Mar 2014 11:27:42 -0700
changeset 191908 6bf817013e40d1f54f0f9a8b6110b03f45caa463
parent 191907 05532668570bf5e1205a54136998bd31963ad321
child 191909 dd25a11b9f63b928c187e80102686600546ed2ea
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, froydnj
bugs969375
milestone30.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 969375 - MIPS port: Added BaselineCompiler code. r=jandem,froydnj
js/src/jit/mips/BaselineCompiler-mips.cpp
js/src/jit/mips/BaselineCompiler-mips.h
js/src/jit/mips/BaselineHelpers-mips.h
js/src/jit/mips/BaselineIC-mips.cpp
js/src/jit/mips/BaselineRegisters-mips.h
js/src/jit/mips/MacroAssembler-mips.cpp
js/src/jit/mips/MacroAssembler-mips.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips/BaselineCompiler-mips.cpp
@@ -0,0 +1,16 @@
+/* -*- 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/mips/BaselineCompiler-mips.h"
+
+using namespace js;
+using namespace js::jit;
+
+BaselineCompilerMIPS::BaselineCompilerMIPS(JSContext *cx, TempAllocator &alloc,
+                                           HandleScript script)
+  : BaselineCompilerShared(cx, alloc, script)
+{
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips/BaselineCompiler-mips.h
@@ -0,0 +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_mips_BaselineCompiler_mips_h
+#define jit_mips_BaselineCompiler_mips_h
+
+#include "jit/shared/BaselineCompiler-shared.h"
+
+namespace js {
+namespace jit {
+
+class BaselineCompilerMIPS : public BaselineCompilerShared
+{
+  protected:
+    BaselineCompilerMIPS(JSContext *cx, TempAllocator &alloc, HandleScript script);
+};
+
+typedef BaselineCompilerMIPS BaselineCompilerSpecific;
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_mips_BaselineCompiler_mips_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips/BaselineHelpers-mips.h
@@ -0,0 +1,333 @@
+/* -*- 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_mips_BaselineHelpers_mips_h
+#define jit_mips_BaselineHelpers_mips_h
+
+#ifdef JS_ION
+#include "jit/BaselineFrame.h"
+#include "jit/BaselineIC.h"
+#include "jit/BaselineRegisters.h"
+#include "jit/IonMacroAssembler.h"
+
+namespace js {
+namespace jit {
+
+// Distance from sp to the top Value inside an IC stub (no return address on
+// the stack on MIPS).
+static const size_t ICStackValueOffset = 0;
+
+inline void
+EmitRestoreTailCallReg(MacroAssembler &masm)
+{
+    // No-op on MIPS because ra register is always holding the return address.
+}
+
+inline void
+EmitRepushTailCallReg(MacroAssembler &masm)
+{
+    // No-op on MIPS because ra register is always holding the return address.
+}
+
+inline void
+EmitCallIC(CodeOffsetLabel *patchOffset, MacroAssembler &masm)
+{
+    // Move ICEntry offset into BaselineStubReg.
+    CodeOffsetLabel offset = masm.movWithPatch(ImmWord(-1), BaselineStubReg);
+    *patchOffset = offset;
+
+    // Load stub pointer into BaselineStubReg.
+    masm.loadPtr(Address(BaselineStubReg, ICEntry::offsetOfFirstStub()), BaselineStubReg);
+
+    // Load stubcode pointer from BaselineStubEntry.
+    // R2 won't be active when we call ICs, so we can use it as scratch.
+    masm.loadPtr(Address(BaselineStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
+
+    // Call the stubcode via a direct jump-and-link
+    masm.call(R2.scratchReg());
+}
+
+inline void
+EmitEnterTypeMonitorIC(MacroAssembler &masm,
+                       size_t monitorStubOffset = ICMonitoredStub::offsetOfFirstMonitorStub())
+{
+    // This is expected to be called from within an IC, when BaselineStubReg
+    // is properly initialized to point to the stub.
+    masm.loadPtr(Address(BaselineStubReg, (uint32_t) monitorStubOffset), BaselineStubReg);
+
+    // Load stubcode pointer from BaselineStubEntry.
+    // R2 won't be active when we call ICs, so we can use it.
+    masm.loadPtr(Address(BaselineStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
+
+    // Jump to the stubcode.
+    masm.branch(R2.scratchReg());
+}
+
+inline void
+EmitReturnFromIC(MacroAssembler &masm)
+{
+    masm.branch(ra);
+}
+
+inline void
+EmitChangeICReturnAddress(MacroAssembler &masm, Register reg)
+{
+    masm.movePtr(reg, ra);
+}
+
+inline void
+EmitTailCallVM(JitCode *target, MacroAssembler &masm, uint32_t argSize)
+{
+    // We assume during this that R0 and R1 have been pushed, and that R2 is
+    // unused.
+    MOZ_ASSERT(R2 == ValueOperand(t7, t6));
+
+    // Compute frame size.
+    masm.movePtr(BaselineFrameReg, t6);
+    masm.addPtr(Imm32(BaselineFrame::FramePointerOffset), t6);
+    masm.subPtr(BaselineStackReg, t6);
+
+    // Store frame size without VMFunction arguments for GC marking.
+    masm.ma_subu(t7, t6, Imm32(argSize));
+    masm.storePtr(t7, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
+
+    // Push frame descriptor and perform the tail call.
+    // BaselineTailCallReg (ra) already contains the return address (as we
+    // keep it there through the stub calls), but the VMWrapper code being
+    // called expects the return address to also be pushed on the stack.
+    MOZ_ASSERT(BaselineTailCallReg == ra);
+    masm.makeFrameDescriptor(t6, IonFrame_BaselineJS);
+    masm.subPtr(Imm32(sizeof(IonCommonFrameLayout)), StackPointer);
+    masm.storePtr(t6, Address(StackPointer, IonCommonFrameLayout::offsetOfDescriptor()));
+    masm.storePtr(ra, Address(StackPointer, IonCommonFrameLayout::offsetOfReturnAddress()));
+
+    masm.branch(target);
+}
+
+inline void
+EmitCreateStubFrameDescriptor(MacroAssembler &masm, Register reg)
+{
+    // Compute stub frame size. We have to add two pointers: the stub reg and
+    // previous frame pointer pushed by EmitEnterStubFrame.
+    masm.movePtr(BaselineFrameReg, reg);
+    masm.addPtr(Imm32(sizeof(intptr_t) * 2), reg);
+    masm.subPtr(BaselineStackReg, reg);
+
+    masm.makeFrameDescriptor(reg, IonFrame_BaselineStub);
+}
+
+inline void
+EmitCallVM(JitCode *target, MacroAssembler &masm)
+{
+    EmitCreateStubFrameDescriptor(masm, t6);
+    masm.push(t6);
+    masm.call(target);
+}
+
+struct BaselineStubFrame {
+    uintptr_t savedFrame;
+    uintptr_t savedStub;
+    uintptr_t returnAddress;
+    uintptr_t descriptor;
+};
+
+static const uint32_t STUB_FRAME_SIZE = sizeof(BaselineStubFrame);
+static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = offsetof(BaselineStubFrame, savedStub);
+
+inline void
+EmitEnterStubFrame(MacroAssembler &masm, Register scratch)
+{
+    MOZ_ASSERT(scratch != BaselineTailCallReg);
+
+    // Compute frame size.
+    masm.movePtr(BaselineFrameReg, scratch);
+    masm.addPtr(Imm32(BaselineFrame::FramePointerOffset), scratch);
+    masm.subPtr(BaselineStackReg, scratch);
+
+    masm.storePtr(scratch, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
+
+    // Note: when making changes here, don't forget to update
+    // BaselineStubFrame if needed.
+
+    // Push frame descriptor and return address.
+    masm.makeFrameDescriptor(scratch, IonFrame_BaselineJS);
+    masm.subPtr(Imm32(STUB_FRAME_SIZE), StackPointer);
+    masm.storePtr(scratch, Address(StackPointer, offsetof(BaselineStubFrame, descriptor)));
+    masm.storePtr(BaselineTailCallReg, Address(StackPointer,
+                                               offsetof(BaselineStubFrame, returnAddress)));
+
+    // Save old frame pointer, stack pointer and stub reg.
+    masm.storePtr(BaselineStubReg, Address(StackPointer,
+                                           offsetof(BaselineStubFrame, savedStub)));
+    masm.storePtr(BaselineFrameReg, Address(StackPointer,
+                                            offsetof(BaselineStubFrame, savedFrame)));
+    masm.movePtr(BaselineStackReg, BaselineFrameReg);
+
+    // We pushed 4 words, so the stack is still aligned to 8 bytes.
+    masm.checkStackAlignment();
+}
+
+inline void
+EmitLeaveStubFrame(MacroAssembler &masm, bool calledIntoIon = false)
+{
+    // Ion frames do not save and restore the frame pointer. If we called
+    // into Ion, we have to restore the stack pointer from the frame descriptor.
+    // If we performed a VM call, the descriptor has been popped already so
+    // in that case we use the frame pointer.
+    if (calledIntoIon) {
+        masm.pop(ScratchRegister);
+        masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), ScratchRegister);
+        masm.addPtr(ScratchRegister, BaselineStackReg);
+    } else {
+        masm.movePtr(BaselineFrameReg, BaselineStackReg);
+    }
+
+    masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedFrame)),
+                 BaselineFrameReg);
+    masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedStub)),
+                 BaselineStubReg);
+
+    // Load the return address.
+    masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, returnAddress)),
+                 BaselineTailCallReg);
+
+    // Discard the frame descriptor.
+    masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, descriptor)), ScratchRegister);
+    masm.addPtr(Imm32(STUB_FRAME_SIZE), StackPointer);
+}
+
+inline void
+EmitStowICValues(MacroAssembler &masm, int values)
+{
+    MOZ_ASSERT(values >= 0 && values <= 2);
+    switch(values) {
+      case 1:
+        // Stow R0
+        masm.pushValue(R0);
+        break;
+      case 2:
+        // Stow R0 and R1
+        masm.pushValue(R0);
+        masm.pushValue(R1);
+        break;
+    }
+}
+
+inline void
+EmitUnstowICValues(MacroAssembler &masm, int values, bool discard = false)
+{
+    MOZ_ASSERT(values >= 0 && values <= 2);
+    switch(values) {
+      case 1:
+        // Unstow R0.
+        if (discard)
+            masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg);
+        else
+            masm.popValue(R0);
+        break;
+      case 2:
+        // Unstow R0 and R1.
+        if (discard) {
+            masm.addPtr(Imm32(sizeof(Value) * 2), BaselineStackReg);
+        } else {
+            masm.popValue(R1);
+            masm.popValue(R0);
+        }
+        break;
+    }
+}
+
+inline void
+EmitCallTypeUpdateIC(MacroAssembler &masm, JitCode *code, uint32_t objectOffset)
+{
+    // R0 contains the value that needs to be typechecked.
+    // The object we're updating is a boxed Value on the stack, at offset
+    // objectOffset from $sp, excluding the return address.
+
+    // Save the current BaselineStubReg to stack, as well as the TailCallReg,
+    // since on mips, the $ra is live.
+    masm.subPtr(Imm32(2 * sizeof(intptr_t)), StackPointer);
+    masm.storePtr(BaselineStubReg, Address(StackPointer, sizeof(intptr_t)));
+    masm.storePtr(BaselineTailCallReg, Address(StackPointer, 0));
+
+    // This is expected to be called from within an IC, when BaselineStubReg
+    // is properly initialized to point to the stub.
+    masm.loadPtr(Address(BaselineStubReg, ICUpdatedStub::offsetOfFirstUpdateStub()),
+                 BaselineStubReg);
+
+    // Load stubcode pointer from BaselineStubReg into BaselineTailCallReg.
+    masm.loadPtr(Address(BaselineStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
+
+    // Call the stubcode.
+    masm.call(R2.scratchReg());
+
+    // Restore the old stub reg and tailcall reg.
+    masm.loadPtr(Address(StackPointer, 0), BaselineTailCallReg);
+    masm.loadPtr(Address(StackPointer, sizeof(intptr_t)), BaselineStubReg);
+    masm.addPtr(Imm32(2 * sizeof(intptr_t)), StackPointer);
+
+    // The update IC will store 0 or 1 in R1.scratchReg() reflecting if the
+    // value in R0 type-checked properly or not.
+    Label success;
+    masm.ma_b(R1.scratchReg(), Imm32(1), &success, Assembler::Equal, ShortJump);
+
+    // If the IC failed, then call the update fallback function.
+    EmitEnterStubFrame(masm, R1.scratchReg());
+
+    masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1);
+
+    masm.pushValue(R0);
+    masm.pushValue(R1);
+    masm.push(BaselineStubReg);
+
+    // Load previous frame pointer, push BaselineFrame *.
+    masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg());
+    masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg());
+
+    EmitCallVM(code, masm);
+    EmitLeaveStubFrame(masm);
+
+    // Success at end.
+    masm.bind(&success);
+}
+
+template <typename AddrType>
+inline void
+EmitPreBarrier(MacroAssembler &masm, const AddrType &addr, MIRType type)
+{
+    // On MIPS, $ra is clobbered by patchableCallPreBarrier. Save it first.
+    masm.push(ra);
+    masm.patchableCallPreBarrier(addr, type);
+    masm.pop(ra);
+}
+
+inline void
+EmitStubGuardFailure(MacroAssembler &masm)
+{
+    // NOTE: This routine assumes that the stub guard code left the stack in
+    // the same state it was in when it was entered.
+
+    // BaselineStubEntry points to the current stub.
+
+    // Load next stub into BaselineStubReg
+    masm.loadPtr(Address(BaselineStubReg, ICStub::offsetOfNext()), BaselineStubReg);
+
+    // Load stubcode pointer from BaselineStubEntry into scratch register.
+    masm.loadPtr(Address(BaselineStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
+
+    // Return address is already loaded, just jump to the next stubcode.
+    MOZ_ASSERT(BaselineTailCallReg == ra);
+    masm.branch(R2.scratchReg());
+}
+
+
+} // namespace jit
+} // namespace js
+
+#endif // JS_ION
+
+#endif /* jit_mips_BaselineHelpers_mips_h */
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips/BaselineIC-mips.cpp
@@ -0,0 +1,223 @@
+/* -*- 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 "jsiter.h"
+
+#include "jit/BaselineCompiler.h"
+#include "jit/BaselineHelpers.h"
+#include "jit/BaselineIC.h"
+#include "jit/BaselineJIT.h"
+#include "jit/IonLinker.h"
+
+#include "jsboolinlines.h"
+
+using namespace js;
+using namespace js::jit;
+
+namespace js {
+namespace jit {
+
+// ICCompare_Int32
+
+bool
+ICCompare_Int32::Compiler::generateStubCode(MacroAssembler &masm)
+{
+    // Guard that R0 is an integer and R1 is an integer.
+    Label failure;
+    Label conditionTrue;
+    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
+    masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+
+    // Compare payload regs of R0 and R1.
+    Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
+    masm.ma_cmp_set(R0.payloadReg(), R0.payloadReg(), R1.payloadReg(), cond);
+
+    masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0);
+    EmitReturnFromIC(masm);
+
+    // Failure case - jump to next stub
+    masm.bind(&failure);
+    EmitStubGuardFailure(masm);
+
+    return true;
+}
+
+bool
+ICCompare_Double::Compiler::generateStubCode(MacroAssembler &masm)
+{
+    Label failure, isNaN;
+    masm.ensureDouble(R0, FloatReg0, &failure);
+    masm.ensureDouble(R1, FloatReg1, &failure);
+
+    Register dest = R0.scratchReg();
+
+    Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(op);
+
+    masm.ma_cmp_set_double(dest, FloatReg0, FloatReg1, doubleCond);
+
+    masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0);
+    EmitReturnFromIC(masm);
+
+    // Failure case - jump to next stub
+    masm.bind(&failure);
+    EmitStubGuardFailure(masm);
+    return true;
+}
+
+// ICBinaryArith_Int32
+
+bool
+ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler &masm)
+{
+    // Guard that R0 is an integer and R1 is an integer.
+    Label failure;
+    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
+    masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+
+    // Add R0 and R1. Don't need to explicitly unbox, just use R2's payloadReg.
+    Register scratchReg = R2.payloadReg();
+
+    // DIV and MOD need an extra non-volatile ValueOperand to hold R0.
+    GeneralRegisterSet savedRegs = availableGeneralRegs(2);
+    savedRegs = GeneralRegisterSet::Intersect(GeneralRegisterSet::NonVolatile(), savedRegs);
+    ValueOperand savedValue = savedRegs.takeAnyValue();
+
+    Label goodMul, divTest1, divTest2;
+    switch(op_) {
+      case JSOP_ADD:
+        // We know R0.typeReg() already contains the integer tag. No boxing
+        // required.
+        masm.ma_addTestOverflow(R0.payloadReg(), R0.payloadReg(), R1.payloadReg(), &failure);
+        break;
+      case JSOP_SUB:
+        masm.ma_subTestOverflow(R0.payloadReg(), R0.payloadReg(), R1.payloadReg(), &failure);
+        break;
+      case JSOP_MUL: {
+        masm.ma_mul_branch_overflow(scratchReg, R0.payloadReg(), R1.payloadReg(), &failure);
+
+        masm.ma_b(scratchReg, Imm32(0), &goodMul, Assembler::NotEqual, ShortJump);
+
+        // Result is -0 if operands have different signs.
+        masm.as_xor(t8, R0.payloadReg(), R1.payloadReg());
+        masm.ma_b(t8, Imm32(0), &failure, Assembler::LessThan, ShortJump);
+
+        masm.bind(&goodMul);
+        masm.move32(scratchReg, R0.payloadReg());
+        break;
+      }
+      case JSOP_DIV:
+      case JSOP_MOD: {
+        // Check for INT_MIN / -1, it results in a double.
+        masm.ma_b(R0.payloadReg(), Imm32(INT_MIN), &divTest1, Assembler::NotEqual, ShortJump);
+        masm.ma_b(R1.payloadReg(), Imm32(-1), &failure, Assembler::Equal, ShortJump);
+        masm.bind(&divTest1);
+
+        // Check for division by zero
+        masm.ma_b(R1.payloadReg(), Imm32(0), &failure, Assembler::Equal, ShortJump);
+
+        // Check for 0 / X with X < 0 (results in -0).
+        masm.ma_b(R0.payloadReg(), Imm32(0), &divTest2, Assembler::NotEqual, ShortJump);
+        masm.ma_b(R1.payloadReg(), Imm32(0), &failure, Assembler::LessThan, ShortJump);
+        masm.bind(&divTest2);
+
+        masm.as_div(R0.payloadReg(), R1.payloadReg());
+
+        if (op_ == JSOP_DIV) {
+            // Result is a double if the remainder != 0.
+            masm.as_mfhi(scratchReg);
+            masm.ma_b(scratchReg, Imm32(0), &failure, Assembler::NotEqual, ShortJump);
+            masm.as_mflo(scratchReg);
+            masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
+        } else {
+            Label done;
+            // If X % Y == 0 and X < 0, the result is -0.
+            masm.as_mfhi(scratchReg);
+            masm.ma_b(scratchReg, Imm32(0), &done, Assembler::NotEqual, ShortJump);
+            masm.ma_b(R0.payloadReg(), Imm32(0), &failure, Assembler::LessThan, ShortJump);
+            masm.bind(&done);
+            masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
+        }
+        break;
+      }
+      case JSOP_BITOR:
+        masm.ma_or(R0.payloadReg() , R0.payloadReg(), R1.payloadReg());
+        break;
+      case JSOP_BITXOR:
+        masm.ma_xor(R0.payloadReg() , R0.payloadReg(), R1.payloadReg());
+        break;
+      case JSOP_BITAND:
+        masm.ma_and(R0.payloadReg() , R0.payloadReg(), R1.payloadReg());
+        break;
+      case JSOP_LSH:
+        // MIPS will only use 5 lowest bits in R1 as shift offset.
+        masm.ma_sll(R0.payloadReg(), R0.payloadReg(), R1.payloadReg());
+        break;
+      case JSOP_RSH:
+        masm.ma_sra(R0.payloadReg(), R0.payloadReg(), R1.payloadReg());
+        break;
+      case JSOP_URSH:
+        masm.ma_srl(scratchReg, R0.payloadReg(), R1.payloadReg());
+        if (allowDouble_) {
+            Label toUint;
+            masm.ma_b(scratchReg, Imm32(0), &toUint, Assembler::LessThan, ShortJump);
+
+            // Move result and box for return.
+            masm.move32(scratchReg, R0.payloadReg());
+            EmitReturnFromIC(masm);
+
+            masm.bind(&toUint);
+            masm.convertUInt32ToDouble(scratchReg, FloatReg1);
+            masm.boxDouble(FloatReg1, R0);
+        } else {
+            masm.ma_b(scratchReg, Imm32(0), &failure, Assembler::LessThan, ShortJump);
+            // Move result for return.
+            masm.move32(scratchReg, R0.payloadReg());
+        }
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Unhandled op for BinaryArith_Int32.");
+    }
+
+    EmitReturnFromIC(masm);
+
+    // Failure case - jump to next stub
+    masm.bind(&failure);
+    EmitStubGuardFailure(masm);
+
+    return true;
+}
+
+bool
+ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler &masm)
+{
+    Label failure;
+    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
+
+    switch (op) {
+      case JSOP_BITNOT:
+        masm.not32(R0.payloadReg());
+        break;
+      case JSOP_NEG:
+        // Guard against 0 and MIN_INT, both result in a double.
+        masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(INT32_MAX), &failure);
+
+        masm.neg32(R0.payloadReg());
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Unexpected op");
+        return false;
+    }
+
+    EmitReturnFromIC(masm);
+
+    masm.bind(&failure);
+    EmitStubGuardFailure(masm);
+    return true;
+}
+
+
+} // namespace jit
+} // namespace js
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips/BaselineRegisters-mips.h
@@ -0,0 +1,49 @@
+/* -*- 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_mips_BaselineRegisters_mips_h
+#define jit_mips_BaselineRegisters_mips_h
+
+#ifdef JS_ION
+
+#include "jit/IonMacroAssembler.h"
+
+namespace js {
+namespace jit {
+
+static MOZ_CONSTEXPR_VAR Register BaselineFrameReg = s5;
+static MOZ_CONSTEXPR_VAR Register BaselineStackReg = sp;
+
+static MOZ_CONSTEXPR_VAR ValueOperand R0(v1, v0);
+static MOZ_CONSTEXPR_VAR ValueOperand R1(s7, s6);
+static MOZ_CONSTEXPR_VAR ValueOperand R2(t7, t6);
+
+// BaselineTailCallReg and BaselineStubReg
+// These use registers that are not preserved across calls.
+static MOZ_CONSTEXPR_VAR Register BaselineTailCallReg = ra;
+static MOZ_CONSTEXPR_VAR Register BaselineStubReg = t5;
+
+static MOZ_CONSTEXPR_VAR Register ExtractTemp0 = InvalidReg;
+static MOZ_CONSTEXPR_VAR Register ExtractTemp1 = InvalidReg;
+
+// Register used internally by MacroAssemblerMIPS.
+static MOZ_CONSTEXPR_VAR Register BaselineSecondScratchReg = SecondScratchReg;
+
+// Note that BaselineTailCallReg is actually just the link register.
+// In MIPS code emission, we do not clobber BaselineTailCallReg since we keep
+// the return address for calls there.
+
+// FloatReg0 must be equal to ReturnFloatReg.
+static MOZ_CONSTEXPR_VAR FloatRegister FloatReg0 = f0;
+static MOZ_CONSTEXPR_VAR FloatRegister FloatReg1 = f2;
+
+} // namespace jit
+} // namespace js
+
+#endif // JS_ION
+
+#endif /* jit_mips_BaselineRegisters_mips_h */
+
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -1581,16 +1581,22 @@ MacroAssemblerMIPSCompat::addPtr(Registe
 void
 MacroAssemblerMIPSCompat::addPtr(const Address &src, Register dest)
 {
     loadPtr(src, ScratchRegister);
     ma_addu(dest, ScratchRegister);
 }
 
 void
+MacroAssemblerMIPSCompat::subPtr(Register src, Register dest)
+{
+    ma_subu(dest, dest, src);
+}
+
+void
 MacroAssemblerMIPSCompat::not32(Register reg)
 {
     ma_not(reg, reg);
 }
 
 // Logical operations
 void
 MacroAssemblerMIPSCompat::and32(Imm32 imm, Register dest)
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -903,16 +903,17 @@ public:
     void xor32(Imm32 imm, Register dest);
     void xorPtr(Imm32 imm, Register dest);
     void xorPtr(Register src, Register dest);
     void orPtr(Imm32 imm, Register dest);
     void orPtr(Register src, Register dest);
     void andPtr(Imm32 imm, Register dest);
     void andPtr(Register src, Register dest);
     void addPtr(Register src, Register dest);
+    void subPtr(Register src, Register dest);
     void addPtr(const Address &src, Register dest);
     void not32(Register reg);
 
     void move32(const Imm32 &imm, const Register &dest);
     void move32(const Register &src, const Register &dest);
 
     void movePtr(const Register &src, const Register &dest);
     void movePtr(const ImmWord &imm, const Register &dest);