Bug 805918 - Baseline Compiler ARM support. r=jandem
authorKannan Vijayan <kvijayan@mozilla.com>
Fri, 16 Nov 2012 13:07:23 -0500
changeset 113170 8c077dce78da37b51d559a2919dc1062fbd6a766
parent 113169 4415df1167d310a847a2630d67d28f65e543e73f
child 113171 1b76643998a6897264238a8b517dc9e52cdf0701
child 113689 338cb56257d0ea1272f430c703ef7cf1fc9903d6
push id1359
push userkvijayan@mozilla.com
push dateFri, 16 Nov 2012 18:07:32 +0000
reviewersjandem
bugs805918
milestone19.0a1
Bug 805918 - Baseline Compiler ARM support. r=jandem
js/src/Makefile.in
js/src/ion/BaselineCompiler.h
js/src/ion/BaselineHelpers.h
js/src/ion/BaselineIC.cpp
js/src/ion/BaselineIC.h
js/src/ion/BaselineJIT.cpp
js/src/ion/BaselineRegisters.h
js/src/ion/arm/BaselineCompiler-arm.cpp
js/src/ion/arm/BaselineCompiler-arm.h
js/src/ion/arm/BaselineHelpers-arm.h
js/src/ion/arm/BaselineIC-arm.cpp
js/src/ion/arm/BaselineRegisters-arm.h
js/src/ion/arm/IonFrames-arm.h
js/src/ion/arm/MacroAssembler-arm.cpp
js/src/ion/arm/MacroAssembler-arm.h
js/src/ion/x64/BaselineHelpers-x64.h
js/src/ion/x64/BaselineIC-x64.cpp
js/src/ion/x86/BaselineHelpers-x86.h
js/src/ion/x86/BaselineIC-x86.cpp
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -366,16 +366,18 @@ CPPSRCS +=	Lowering-arm.cpp \
 		CodeGenerator-arm.cpp \
 		Trampoline-arm.cpp \
 		Assembler-arm.cpp \
 		Bailouts-arm.cpp \
 		IonFrames-arm.cpp \
 		MoveEmitter-arm.cpp \
 		Architecture-arm.cpp \
 		MacroAssembler-arm.cpp \
+		BaselineCompiler-arm.cpp \
+		BaselineIC-arm.cpp \
 		$(NULL)
 endif #ENABLE_ION
 endif
 endif #ENABLE_ION
 ifeq (sparc, $(findstring sparc,$(TARGET_CPU)))
 ASFILES +=	TrampolineSparc.s
 endif
 ifeq (mips, $(findstring mips,$(TARGET_CPU)))
--- a/js/src/ion/BaselineCompiler.h
+++ b/js/src/ion/BaselineCompiler.h
@@ -18,17 +18,17 @@
 #include "BaselineIC.h"
 #include "FixedList.h"
 
 #if defined(JS_CPU_X86)
 # include "x86/BaselineCompiler-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/BaselineCompiler-x64.h"
 #else
-#error "CPU Not Supported"
+# include "arm/BaselineCompiler-arm.h"
 #endif
 
 namespace js {
 namespace ion {
 
 #define OPCODE_LIST(_)         \
     _(JSOP_NOP)                \
     _(JSOP_LABEL)              \
--- a/js/src/ion/BaselineHelpers.h
+++ b/js/src/ion/BaselineHelpers.h
@@ -8,17 +8,17 @@
 #if !defined(jsion_baseline_helpers_h__) && defined(JS_ION)
 #define jsion_baseline_helpers_h__
 
 #if defined(JS_CPU_X86)
 # include "x86/BaselineHelpers-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/BaselineHelpers-x64.h"
 #else
-#error "CPU Not Supported"
+# include "arm/BaselineHelpers-arm.h"
 #endif
 
 namespace js {
 namespace ion {
 
 } // namespace ion
 } // namespace js
 
--- a/js/src/ion/BaselineIC.cpp
+++ b/js/src/ion/BaselineIC.cpp
@@ -12,16 +12,44 @@
 #include "IonLinker.h"
 #include "IonSpewer.h"
 #include "VMFunctions.h"
 #include "IonFrames-inl.h"
 
 namespace js {
 namespace ion {
 
+IonCode *
+ICStubCompiler::getStubCode()
+{
+    IonCompartment *ion = cx->compartment->ionCompartment();
+
+    // Check for existing cached stubcode.
+    uint32_t stubKey = getKey();
+    IonCode *stubCode = ion->getStubCode(stubKey);
+    if (stubCode)
+        return stubCode;
+
+    // Compile new stubcode.
+    MacroAssembler masm;
+    AutoFlushCache afc("ICStubCompiler::getStubCode", ion);
+    if (!generateStubCode(masm))
+        return NULL;
+    Linker linker(masm);
+    Rooted<IonCode *> newStubCode(cx, linker.newCode(cx));
+    if (!newStubCode)
+        return NULL;
+
+    // Cache newly compiled stubcode.
+    if (!ion->putStubCode(stubKey, newStubCode))
+        return NULL;
+
+    return newStubCode;
+}
+
 bool
 ICStubCompiler::callVM(const VMFunction &fun, MacroAssembler &masm)
 {
     IonCompartment *ion = cx->compartment->ionCompartment();
     IonCode *code = ion->generateVMWrapper(cx, fun);
     if (!code)
         return false;
 
@@ -83,41 +111,39 @@ DoCompareFallback(JSContext *cx, ICCompa
 
             stub->addNewStub(int32Stub);
         }
     }
 
     return true;
 }
 
-IonCode *
-ICCompare_Fallback::Compiler::generateStubCode()
+bool
+ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
 {
-    MacroAssembler masm;
     JS_ASSERT(R0 == JSReturnOperand);
 
-    // Pop return address.
-    masm.pop(BaselineTailCallReg);
+    // Restore the tail call register.
+    EmitRestoreTailCallReg(masm);
 
     // Get VMFunction to call
     typedef bool (*pf)(JSContext *, ICCompare_Fallback *, HandleValue, HandleValue,
                        MutableHandleValue);
     static const VMFunction fun = FunctionInfo<pf>(DoCompareFallback);
 
     // Push arguments.
     masm.pushValue(R1);
     masm.pushValue(R0);
     masm.push(BaselineStubReg);
 
     // Call.
     if (!callVM(fun, masm))
-        return NULL;
+        return false;
 
-    Linker linker(masm);
-    return linker.newCode(cx);
+    return true;
 }
 
 //
 // ToBool_Fallback
 //
 
 static bool
 DoToBoolFallback(JSContext *cx, ICToBool_Fallback *stub, HandleValue arg, MutableHandleValue ret)
@@ -141,93 +167,90 @@ DoToBoolFallback(JSContext *cx, ICToBool
             return false;
 
         stub->addNewStub(boolStub);
     }
 
     return true;
 }
 
-IonCode *
-ICToBool_Fallback::Compiler::generateStubCode()
+bool
+ICToBool_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
 {
-    MacroAssembler masm;
     JS_ASSERT(R0 == JSReturnOperand);
 
-    // Pop return address.
-    masm.pop(BaselineTailCallReg);
+    // Restore the tail call register.
+    EmitRestoreTailCallReg(masm);
 
     // Get VMFunction to call
     typedef bool (*pf)(JSContext *, ICToBool_Fallback *, HandleValue, MutableHandleValue);
     static const VMFunction fun = FunctionInfo<pf>(DoToBoolFallback);
 
     // Push arguments.
     masm.pushValue(R0);
     masm.push(BaselineStubReg);
 
     // Call.
     if (!callVM(fun, masm))
-        return NULL;
+        return false;
 
-    Linker linker(masm);
-    return linker.newCode(cx);
+    return true;
 }
 
 //
 // ToBool_Bool
 //
 
-IonCode *
-ICToBool_Bool::Compiler::generateStubCode()
+bool
+ICToBool_Bool::Compiler::generateStubCode(MacroAssembler &masm)
 {
-    MacroAssembler masm;
-
     // Just guard that R0 is a boolean and leave it be if so.
     Label failure;
     masm.branchTestBoolean(Assembler::NotEqual, R0, &failure);
-    masm.ret();
+    EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
 
-    Linker linker(masm);
-    return linker.newCode(cx);
+    return true;
 }
 
+//
+// ToNumber_Fallback
+//
+
 static bool
 DoToNumberFallback(JSContext *cx, ICToNumber_Fallback *stub, HandleValue arg, MutableHandleValue ret)
 {
     ret.set(arg);
     return ToNumber(cx, ret.address());
 }
 
-IonCode *
-ICToNumber_Fallback::Compiler::generateStubCode()
+bool
+ICToNumber_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
 {
-    MacroAssembler masm;
     JS_ASSERT(R0 == JSReturnOperand);
 
-    // Pop return address.
-    masm.pop(BaselineTailCallReg);
+    // Restore the tail call register.
+    EmitRestoreTailCallReg(masm);
 
     // Get VMFunction to call
     typedef bool (*pf)(JSContext *, ICToNumber_Fallback *, HandleValue, MutableHandleValue);
     static const VMFunction fun = FunctionInfo<pf>(DoToNumberFallback);
 
     // Push arguments.
     masm.pushValue(R0);
     masm.push(BaselineStubReg);
 
     // Call.
     if (!callVM(fun, masm))
-        return NULL;
+        return false;
 
-    Linker linker(masm);
-    return linker.newCode(cx);
+    return true;
 }
 
 //
 // BinaryArith_Fallback
 //
 
 static bool
 DoBinaryArithFallback(JSContext *cx, ICBinaryArith_Fallback *stub, HandleValue lhs,
@@ -267,43 +290,45 @@ DoBinaryArithFallback(JSContext *cx, ICB
 
             stub->addNewStub(int32Stub);
         }
     }
 
     return true;
 }
 
-IonCode *
-ICBinaryArith_Fallback::Compiler::generateStubCode()
+bool
+ICBinaryArith_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
 {
-    MacroAssembler masm;
     JS_ASSERT(R0 == JSReturnOperand);
 
-    // Pop return address.
-    masm.pop(BaselineTailCallReg);
+    // Restore the tail call register.
+    EmitRestoreTailCallReg(masm);
 
     // Get VMFunction to call
     typedef bool (*pf)(JSContext *, ICBinaryArith_Fallback *, HandleValue, HandleValue,
                        MutableHandleValue);
     static const VMFunction fun = FunctionInfo<pf>(DoBinaryArithFallback);
 
     // Push arguments.
     masm.pushValue(R1);
     masm.pushValue(R0);
     masm.push(BaselineStubReg);
 
     // Call.
     if (!callVM(fun, masm))
-        return NULL;
+        return false;
 
-    Linker linker(masm);
-    return linker.newCode(cx);
+    return true;
 }
 
+//
+// Call_Fallback
+//
+
 static bool
 DoCallFallback(JSContext *cx, ICCall_Fallback *stub, uint32_t argc, Value *vp, MutableHandleValue res)
 {
     RootedValue callee(cx, vp[0]);
     RootedValue thisv(cx, vp[1]);
 
     Value *args = vp + 2;
 
@@ -350,29 +375,28 @@ ICCallStubCompiler::pushCallArguments(Ma
     Label loop, done;
     masm.bind(&loop);
     masm.branchTest32(Assembler::Zero, count, count, &done);
     {
         masm.pushValue(Address(argPtr, 0));
         masm.addPtr(Imm32(sizeof(Value)), argPtr);
 
         masm.sub32(Imm32(1), count);
-        masm.jmp(&loop);
+        masm.jump(&loop);
     }
     masm.bind(&done);
 }
 
-IonCode *
-ICCall_Fallback::Compiler::generateStubCode()
+bool
+ICCall_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
 {
-    MacroAssembler masm;
     JS_ASSERT(R0 == JSReturnOperand);
 
-    // Pop return address.
-    masm.pop(BaselineTailCallReg);
+    // Restore the tail call register.
+    EmitRestoreTailCallReg(masm);
 
     typedef bool (*pf)(JSContext *, ICCall_Fallback *, uint32_t, Value *, MutableHandleValue);
     static const VMFunction fun = FunctionInfo<pf>(DoCallFallback);
 
     // Values are on the stack left-to-right. Calling convention wants them
     // right-to-left so duplicate them on the stack in reverse order.
     // |this| and callee are pushed last.
 
@@ -380,16 +404,15 @@ ICCall_Fallback::Compiler::generateStubC
     pushCallArguments(masm, R0.scratchReg());
 
     masm.push(BaselineStackReg);
     masm.push(R0.scratchReg());
     masm.push(BaselineStubReg);
 
     // Call.
     if (!callVM(fun, masm))
-        return NULL;
+        return false;
 
-    Linker linker(masm);
-    return linker.newCode(cx);
+    return true;
 }
 
 } // namespace ion
 } // namespace js
--- a/js/src/ion/BaselineIC.h
+++ b/js/src/ion/BaselineIC.h
@@ -338,33 +338,18 @@ class ICStubCompiler
     JSContext *     cx;
     ICStub::Kind    kind;
 
     // By default the stubcode key is just the kind.
     virtual int32_t getKey() const {
         return static_cast<int32_t>(kind);
     }
 
-    virtual IonCode *generateStubCode() = 0;
-    IonCode *getStubCode() {
-        IonCompartment *ion = cx->compartment->ionCompartment();
-        uint32_t stubKey = getKey();
-        IonCode *stubCode = ion->getStubCode(stubKey);
-        if (stubCode)
-            return stubCode;
-
-        Rooted<IonCode *> newStubCode(cx, generateStubCode());
-        if (!newStubCode)
-            return NULL;
-
-        if (!ion->putStubCode(stubKey, newStubCode))
-            return NULL;
-
-        return newStubCode;
-    }
+    virtual bool generateStubCode(MacroAssembler &masm) = 0;
+    IonCode *getStubCode();
 
     ICStubCompiler(JSContext *cx, ICStub::Kind kind)
       : cx(cx), kind(kind) {}
 
     // Helper to generate an stubcall IonCode from a VMFunction wrapper.
     bool callVM(const VMFunction &fun, MacroAssembler &masm);
 
     GeneralRegisterSet availableGeneralRegs() const {
@@ -410,17 +395,17 @@ class ICCompare_Fallback : public ICFall
 
     static inline ICCompare_Fallback *New(IonCode *code) {
         return new ICCompare_Fallback(code);
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
-        IonCode *generateStubCode();
+        bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx)
           : ICStubCompiler(cx, ICStub::Compare_Fallback) {}
 
         ICStub *getStub() {
             return ICCompare_Fallback::New(getStubCode());
         }
@@ -435,17 +420,17 @@ class ICCompare_Int32 : public ICFallbac
   public:
     static inline ICCompare_Int32 *New(IonCode *code) {
         return new ICCompare_Int32(code);
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICMultiStubCompiler {
       protected:
-        IonCode *generateStubCode();
+        bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx, JSOp op)
           : ICMultiStubCompiler(cx, ICStub::Compare_Int32, op) {}
 
         ICStub *getStub() {
             return ICCompare_Int32::New(getStubCode());
         }
@@ -465,17 +450,17 @@ class ICToBool_Fallback : public ICFallb
 
     static inline ICToBool_Fallback *New(IonCode *code) {
         return new ICToBool_Fallback(code);
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
-        IonCode *generateStubCode();
+        bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx)
           : ICStubCompiler(cx, ICStub::ToBool_Fallback) {}
 
         ICStub *getStub() {
             return ICToBool_Fallback::New(getStubCode());
         }
@@ -490,17 +475,17 @@ class ICToBool_Bool : public ICStub
   public:
     static inline ICToBool_Bool *New(IonCode *code) {
         return new ICToBool_Bool(code);
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
-        IonCode *generateStubCode();
+        bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx)
           : ICStubCompiler(cx, ICStub::ToBool_Bool) {}
 
         ICStub *getStub() {
             return ICToBool_Bool::New(getStubCode());
         }
@@ -518,17 +503,17 @@ class ICToNumber_Fallback : public ICFal
   public:
     static inline ICToNumber_Fallback *New(IonCode *code) {
         return new ICToNumber_Fallback(code);
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
-        IonCode *generateStubCode();
+        bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx)
           : ICStubCompiler(cx, ICStub::ToNumber_Fallback) {}
 
         ICStub *getStub() {
             return ICToNumber_Fallback::New(getStubCode());
         }
@@ -548,17 +533,17 @@ class ICBinaryArith_Fallback : public IC
 
     static inline ICBinaryArith_Fallback *New(IonCode *code) {
         return new ICBinaryArith_Fallback(code);
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
-        IonCode *generateStubCode();
+        bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx)
           : ICStubCompiler(cx, ICStub::BinaryArith_Fallback) {}
 
         ICStub *getStub() {
             return ICBinaryArith_Fallback::New(getStubCode());
         }
@@ -573,17 +558,17 @@ class ICBinaryArith_Int32 : public ICStu
   public:
     static inline ICBinaryArith_Int32 *New(IonCode *code) {
         return new ICBinaryArith_Int32(code);
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICMultiStubCompiler {
       protected:
-        IonCode *generateStubCode();
+        bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx, JSOp op)
           : ICMultiStubCompiler(cx, ICStub::BinaryArith_Int32, op) {}
 
         ICStub *getStub() {
             return ICBinaryArith_Int32::New(getStubCode());
         }
@@ -609,17 +594,17 @@ class ICCall_Fallback : public ICFallbac
   public:
     static inline ICCall_Fallback *New(IonCode *code) {
         return new ICCall_Fallback(code);
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICCallStubCompiler {
       protected:
-        IonCode *generateStubCode();
+        bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx)
           : ICCallStubCompiler(cx, ICStub::Call_Fallback)
         { }
 
         ICStub *getStub() {
             return ICCall_Fallback::New(getStubCode());
--- a/js/src/ion/BaselineJIT.cpp
+++ b/js/src/ion/BaselineJIT.cpp
@@ -172,16 +172,17 @@ BaselineCompile(JSContext *cx, HandleScr
         return Method_Error;
 
     IonContext ictx(cx, cx->compartment, temp);
 
     BaselineCompiler compiler(cx, script);
     if (!compiler.init())
         return Method_Error;
 
+    AutoFlushCache afc("BaselineJIT", cx->compartment->ionCompartment());
     return compiler.compile();
 }
 
 MethodStatus
 ion::CanEnterBaselineJIT(JSContext *cx, HandleScript script, StackFrame *fp)
 {
     if (cx->compartment->debugMode()) {
         IonSpew(IonSpew_Abort, "BASELINE FIXME: Not compiling in debug mode!");
--- a/js/src/ion/BaselineRegisters.h
+++ b/js/src/ion/BaselineRegisters.h
@@ -8,17 +8,17 @@
 #if !defined(jsion_baseline_registers_h__) && defined(JS_ION)
 #define jsion_baseline_registers_h__
 
 #if defined(JS_CPU_X86)
 # include "x86/BaselineRegisters-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/BaselineRegisters-x64.h"
 #else
-#error "CPU Not Supported"
+# include "arm/BaselineRegisters-arm.h"
 #endif
 
 namespace js {
 namespace ion {
 
 } // namespace ion
 } // namespace js
 
new file mode 100644
--- /dev/null
+++ b/js/src/ion/arm/BaselineCompiler-arm.cpp
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et 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 "BaselineCompiler-arm.h"
+
+using namespace js;
+using namespace js::ion;
+
+BaselineCompilerARM::BaselineCompilerARM(JSContext *cx, JSScript *script)
+  : BaselineCompilerShared(cx, script)
+{
+}
new file mode 100644
--- /dev/null
+++ b/js/src/ion/arm/BaselineCompiler-arm.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et 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 jsion_baselinecompiler_arm_h__
+#define jsion_baselinecompiler_arm_h__
+
+#include "ion/shared/BaselineCompiler-shared.h"
+
+namespace js {
+namespace ion {
+
+class BaselineCompilerARM : public BaselineCompilerShared
+{
+  protected:
+    BaselineCompilerARM(JSContext *cx, JSScript *script);
+};
+
+typedef BaselineCompilerARM BaselineCompilerSpecific;
+
+} // namespace ion
+} // namespace js
+
+#endif // jsion_baselinecompiler_arm_h__
new file mode 100644
--- /dev/null
+++ b/js/src/ion/arm/BaselineHelpers-arm.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et 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/. */
+
+#if !defined(jsion_baseline_helpers_arm_h__) && defined(JS_ION)
+#define jsion_baseline_helpers_arm_h__
+
+#include "ion/IonMacroAssembler.h"
+#include "ion/BaselineRegisters.h"
+#include "ion/BaselineIC.h"
+
+namespace js {
+namespace ion {
+
+inline void
+EmitRestoreTailCallReg(MacroAssembler &masm)
+{
+    // No-op on ARM because link 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 r0.
+    JS_ASSERT(R2 == ValueOperand(r1, r0));
+    masm.loadPtr(Address(BaselineStubReg, ICStub::offsetOfStubCode()), r0);
+
+    // Call the stubcode via a direct branch-and-link
+    masm.ma_blx(r0);
+}
+
+inline void
+EmitReturnFromIC(MacroAssembler &masm)
+{
+    masm.ma_mov(lr, pc);
+}
+
+inline void
+EmitTailCall(IonCode *target, MacroAssembler &masm, uint32_t argSize)
+{
+    // We assume during this that R0 and R1 have been pushed, and that R2 is
+    // unused.
+    JS_ASSERT(R2 == ValueOperand(r1, r0));
+
+    // Compute frame size.
+    masm.movePtr(BaselineFrameReg, r0);
+    masm.ma_add(Imm32(BaselineFrame::FramePointerOffset), r0);
+    masm.ma_sub(BaselineStackReg, r0);
+
+    // Store frame size without VMFunction arguments for GC marking.
+    masm.ma_sub(r0, Imm32(argSize), r1);
+    masm.storePtr(r1, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
+
+    // Push frame descriptor and perform the tail call.
+    // BaselineTailCallReg (lr) 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.
+    JS_ASSERT(BaselineTailCallReg == lr);
+    masm.makeFrameDescriptor(r0, IonFrame_BaselineJS);
+    masm.push(r0);
+    masm.push(lr);
+    masm.branch(target);
+}
+
+inline void
+EmitStubGuardFailure(MacroAssembler &masm)
+{
+    JS_ASSERT(R2 == ValueOperand(r1, r0));
+
+    // 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()), r0);
+
+    // Return address is already loaded, just jump to the next stubcode.
+    JS_ASSERT(BaselineTailCallReg == lr);
+    masm.branch(r0);
+}
+
+
+} // namespace ion
+} // namespace js
+
+#endif
+
new file mode 100644
--- /dev/null
+++ b/js/src/ion/arm/BaselineIC-arm.cpp
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et 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 "ion/BaselineJIT.h"
+#include "ion/BaselineIC.h"
+#include "ion/BaselineCompiler.h"
+#include "ion/BaselineHelpers.h"
+#include "ion/IonLinker.h"
+
+using namespace js;
+using namespace js::ion;
+
+namespace js {
+namespace ion {
+
+// ICCompare_Int32
+
+bool
+ICCompare_Int32::Compiler::generateStubCode(MacroAssembler &masm)
+{
+    // The condition to test on depends on the opcode
+    Assembler::Condition cond;
+    Assembler::Condition notcond;
+    switch(op) {
+      case JSOP_LT:
+        cond = Assembler::LessThan;
+        break;
+      case JSOP_GT:
+        cond = Assembler::GreaterThan;
+        break;
+      default:
+        JS_ASSERT(!"Unhandled op for ICCompare_Int32!");
+        return false;
+    }
+
+    // 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);
+
+    // Compare payload regs of R0 and R1.
+    masm.cmp32(R0.payloadReg(), R1.payloadReg());
+    masm.ma_mov(Imm32(1), R0.payloadReg(), NoSetCond, cond);
+    masm.ma_mov(Imm32(0), R0.payloadReg(), NoSetCond, Assembler::InvertCondition(cond));
+
+    // Result is implicitly boxed already.
+    masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), 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();
+
+    switch(op) {
+      case JSOP_ADD:
+        masm.ma_add(R0.payloadReg(), R1.payloadReg(), scratchReg);
+        break;
+      default:
+        JS_ASSERT(!"Unhandled op for BinaryArith_Int32!");
+        return false;
+    }
+
+    // Just jump to failure on overflow.  R0 and R1 are preserved, so we can just jump to
+    // the next stub.
+    masm.j(Assembler::Overflow, &failure);
+
+    // Box the result and return.  We know R0.typeReg() already contains the integer
+    // tag, so we just need to move the result value into place.
+    masm.movePtr(scratchReg, R0.payloadReg());
+    EmitReturnFromIC(masm);
+
+    // Failure case - jump to next stub
+    masm.bind(&failure);
+    EmitStubGuardFailure(masm);
+
+    return true;
+}
+
+
+} // namespace ion
+} // namespace js
new file mode 100644
--- /dev/null
+++ b/js/src/ion/arm/BaselineRegisters-arm.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et 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/. */
+
+#if !defined(jsion_baseline_registers_arm_h__) && defined(JS_ION)
+#define jsion_baseline_registers_arm_h__
+
+#include "ion/IonMacroAssembler.h"
+
+namespace js {
+namespace ion {
+
+// r15 = program-counter
+// r14 = link-register
+
+// r13 = stack-pointer
+// r11 = frame-pointer
+static const Register BaselineFrameReg = r11;
+static const Register BaselineStackReg = sp;
+
+// ValueOperands R0, R1, and R2.
+// R0 == JSReturnReg, and R2 uses registers not
+// preserved across calls.  R1 value should be
+// preserved across calls.
+static const ValueOperand R0(r3, r2);
+static const ValueOperand R1(r5, r4);
+static const ValueOperand R2(r1, r0);
+
+// BaselineTailCallReg and BaselineStubReg
+// These use registers that are not preserved across
+// calls.
+static const Register BaselineTailCallReg = r14;
+static const Register BaselineStubReg     = r9;
+
+// R6 - R9 are generally available for use within stubcode.
+
+// Note that BaselineTailCallReg is actually just the link
+// register.  In ARM code emission, we do not clobber BaselineTailCallReg
+// since we keep the return address for calls there.
+
+} // namespace ion
+} // namespace js
+
+#endif
+
--- a/js/src/ion/arm/IonFrames-arm.h
+++ b/js/src/ion/arm/IonFrames-arm.h
@@ -78,16 +78,19 @@ class IonJSFrameLayout : public IonEntry
     void replaceCalleeToken(void *calleeToken) {
         calleeToken_ = calleeToken;
     }
     static size_t offsetOfActualArgs() {
         IonJSFrameLayout *base = NULL;
         // +1 to skip |this|.
         return reinterpret_cast<size_t>(&base->argv()[1]);
     }
+    static size_t offsetOfActualArg(size_t arg) {
+        return offsetOfActualArgs() + arg * sizeof(Value);
+    }
 
     Value *argv() {
         return (Value *)(this + 1);
     }
     uintptr_t numActualArgs() const {
         return numActualArgs_;
     }
 
@@ -428,9 +431,44 @@ class InvalidationBailoutStack
     }
 
     void checkInvariants() const;
 };
 
 } // namespace ion
 } // namespace js
 
+// The stack looks like this, fp is the frame pointer:
+//
+// fp+y   arguments
+// fp+x   IonJSFrameLayout (frame header)
+// fp  => saved frame pointer
+// fp-x   BaselineFrame
+//        locals
+//        stack values
+class BaselineFrame
+{
+    size_t frameSize;
+
+  public:
+    // Distance between the frame pointer and the frame header (return address).
+    // This is the old frame pointer saved in the prologue.
+    static const uint32_t FramePointerOffset = sizeof(void *);
+
+    static inline size_t offsetOfArg(size_t index) {
+        return FramePointerOffset + js::ion::IonJSFrameLayout::offsetOfActualArg(index);
+    }
+    static size_t Size() {
+        return sizeof(BaselineFrame);
+    }
+
+    // The reverseOffsetOf methods below compute the offset relative to the
+    // frame's base pointer. Since the stack grows down, these offsets are
+    // negative.
+    static inline size_t reverseOffsetOfFrameSize() {
+        return -(sizeof(BaselineFrame) - offsetof(BaselineFrame, frameSize));
+    }
+    static inline size_t reverseOffsetOfLocal(size_t index) {
+        return -(sizeof(BaselineFrame) + index * sizeof(js::Value)) - sizeof(js::Value);
+    }
+};
+
 #endif // jsion_ionframes_arm_h
--- a/js/src/ion/arm/MacroAssembler-arm.cpp
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp
@@ -1103,16 +1103,22 @@ MacroAssemblerARM::ma_b(void *target, Re
 // This is almost NEVER necessary: we'll basically never be calling a label,
 // except possibly in the crazy bailout-table case.
 void
 MacroAssemblerARM::ma_bl(Label *dest, Assembler::Condition c)
 {
     as_bl(dest, c);
 }
 
+void
+MacroAssemblerARM::ma_blx(Register reg, Assembler::Condition c)
+{
+    as_blx(reg, c);
+}
+
 // VFP/ALU
 void
 MacroAssemblerARM::ma_vadd(FloatRegister src1, FloatRegister src2, FloatRegister dst)
 {
     as_vadd(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2));
 }
 
 void
@@ -2016,16 +2022,22 @@ MacroAssemblerARMCompat::testString(Asse
 
 Assembler::Condition
 MacroAssemblerARMCompat::testObject(Assembler::Condition cond, const ValueOperand &value)
 {
     return testObject(cond, value.typeReg());
 }
 
 Assembler::Condition
+MacroAssemblerARMCompat::testNumber(Assembler::Condition cond, const ValueOperand &value)
+{
+    return testNumber(cond, value.typeReg());
+}
+
+Assembler::Condition
 MacroAssemblerARMCompat::testMagic(Assembler::Condition cond, const ValueOperand &value)
 {
     return testMagic(cond, value.typeReg());
 }
 
 Assembler::Condition
 MacroAssemblerARMCompat::testPrimitive(Assembler::Condition cond, const ValueOperand &value)
 {
@@ -2483,16 +2495,29 @@ MacroAssemblerARMCompat::tagValue(JSValu
 }
 
 void
 MacroAssemblerARMCompat::pushValue(ValueOperand val) {
     ma_push(val.typeReg());
     ma_push(val.payloadReg());
 }
 void
+MacroAssemblerARMCompat::pushValue(const Address &addr)
+{
+    Operand srcOp = Operand(addr);
+    Operand payload = ToPayload(srcOp);
+    Operand type = ToType(srcOp);
+
+    ma_ldr(payload, ScratchRegister);
+    ma_push(ScratchRegister);
+    ma_ldr(type, ScratchRegister);
+    ma_push(ScratchRegister);
+}
+
+void
 MacroAssemblerARMCompat::popValue(ValueOperand val) {
     ma_pop(val.payloadReg());
     ma_pop(val.typeReg());
 }
 void
 MacroAssemblerARMCompat::storePayload(const Value &val, Operand dest)
 {
     jsval_layout jv = JSVAL_TO_IMPL(val);
--- a/js/src/ion/arm/MacroAssembler-arm.h
+++ b/js/src/ion/arm/MacroAssembler-arm.h
@@ -37,16 +37,17 @@ class MacroAssemblerARM : public Assembl
 
     // somewhat direct wrappers for the low-level assembler funcitons
     // bitops
     // attempt to encode a virtual alu instruction using
     // two real instructions.
   private:
     bool alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op,
                  SetCond_ sc, Condition c);
+
   public:
     void ma_alu(Register src1, Operand2 op2, Register dest, ALUOp op,
                 SetCond_ sc = NoSetCond, Condition c = Always);
     void ma_alu(Register src1, Imm32 imm, Register dest,
                 ALUOp op,
                 SetCond_ sc =  NoSetCond, Condition c = Always);
 
     void ma_alu(Register src1, Operand op2, Register dest, ALUOp op,
@@ -253,16 +254,18 @@ class MacroAssemblerARM : public Assembl
     void ma_bx(Register dest, Condition c = Always);
 
     void ma_b(void *target, Relocation::Kind reloc, Condition c = Always);
 
     // this is almost NEVER necessary, we'll basically never be calling a label
     // except, possibly in the crazy bailout-table case.
     void ma_bl(Label *dest, Condition c = Always);
 
+    void ma_blx(Register dest, Condition c = Always);
+
     //VFP/ALU
     void ma_vadd(FloatRegister src1, FloatRegister src2, FloatRegister dst);
     void ma_vsub(FloatRegister src1, FloatRegister src2, FloatRegister dst);
 
     void ma_vmul(FloatRegister src1, FloatRegister src2, FloatRegister dst);
     void ma_vdiv(FloatRegister src1, FloatRegister src2, FloatRegister dst);
 
     void ma_vneg(FloatRegister src, FloatRegister dest);
@@ -419,16 +422,19 @@ class MacroAssemblerARMCompat : public M
         ma_callIonHalfPush(ScratchRegister);
     }
     void branch(IonCode *c) {
         BufferOffset bo = m_buffer.nextOffset();
         addPendingJump(bo, c->raw(), Relocation::IONCODE);
         ma_mov(Imm32((uint32)c->raw()), ScratchRegister);
         ma_bx(ScratchRegister);
     }
+    void branch(const Register reg) {
+        ma_bx(reg);
+    }
     void nop() {
         ma_nop();
     }
     void ret() {
         ma_pop(pc);
         m_buffer.markGuard();
     }
     void retn(Imm32 n) {
@@ -474,16 +480,22 @@ class MacroAssemblerARMCompat : public M
 
     CodeOffsetLabel pushWithPatch(ImmWord imm) {
         CodeOffsetLabel label = currentOffset();
         ma_movPatchable(Imm32(imm.value), ScratchRegister, Always, L_MOVWT);
         ma_push(ScratchRegister);
         return label;
     }
 
+    CodeOffsetLabel movWithPatch(ImmWord imm, Register dest) {
+        CodeOffsetLabel label = currentOffset();
+        ma_movPatchable(Imm32(imm.value), dest, Always, L_MOVWT);
+        return label;
+    }
+
     void jump(Label *label) {
         as_b(label);
     }
 
     void neg32(Register reg) {
         ma_neg(reg, reg, SetCond);
     }
     void test32(Register lhs, Register rhs) {
@@ -501,16 +513,17 @@ class MacroAssemblerARMCompat : public M
     // higher level tag testing code
     Condition testInt32(Condition cond, const ValueOperand &value);
     Condition testBoolean(Condition cond, const ValueOperand &value);
     Condition testDouble(Condition cond, const ValueOperand &value);
     Condition testNull(Condition cond, const ValueOperand &value);
     Condition testUndefined(Condition cond, const ValueOperand &value);
     Condition testString(Condition cond, const ValueOperand &value);
     Condition testObject(Condition cond, const ValueOperand &value);
+    Condition testNumber(Condition cond, const ValueOperand &value);
     Condition testMagic(Condition cond, const ValueOperand &value);
 
     Condition testPrimitive(Condition cond, const ValueOperand &value);
 
     // register-based tests
     Condition testInt32(Condition cond, const Register &tag);
     Condition testBoolean(Condition cond, const Register &tag);
     Condition testNull(Condition cond, const Register &tag);
@@ -743,16 +756,25 @@ class MacroAssemblerARMCompat : public M
         if (dest.isFloat())
             loadInt32OrDouble(address.base, address.index, dest.fpu(), address.scale);
         else
             load32(address, dest.gpr());
     }
 
     void moveValue(const Value &val, const ValueOperand &dest);
 
+    void moveValue(const ValueOperand &src, const ValueOperand &dest) {
+        JS_ASSERT(src.typeReg() != dest.payloadReg());
+        JS_ASSERT(src.payloadReg() != dest.typeReg());
+        if (src.typeReg() != dest.typeReg())
+            ma_mov(src.typeReg(), dest.typeReg());
+        if (src.payloadReg() != dest.payloadReg())
+            ma_mov(src.payloadReg(), dest.payloadReg());
+    }
+
     void storeValue(ValueOperand val, Operand dst);
     void storeValue(ValueOperand val, Register base, Register index, int32 shift = defaultShift);
     void storeValue(ValueOperand val, const Address &dest) {
         storeValue(val, Operand(dest));
     }
     void storeValue(JSValueType type, Register reg, Address dest) {
         ma_mov(ImmTag(JSVAL_TYPE_TO_TAG(type)), lr);
         ma_str(lr, Address(dest.base, dest.offset + 4));
@@ -805,16 +827,17 @@ class MacroAssemblerARMCompat : public M
             push(ImmGCPtr(reinterpret_cast<gc::Cell *>(val.toGCThing())));
         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 storePayload(const Value &val, Operand dest);
     void storePayload(Register src, Operand dest);
     void storePayload(const Value &val, Register base, Register index, int32 shift = defaultShift);
     void storePayload(Register src, Register base, Register index, int32 shift = defaultShift);
     void storeTypeTag(ImmTag tag, Operand dest);
     void storeTypeTag(ImmTag tag, Register base, Register index, int32 shift = defaultShift);
 
     void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
--- a/js/src/ion/x64/BaselineHelpers-x64.h
+++ b/js/src/ion/x64/BaselineHelpers-x64.h
@@ -11,16 +11,22 @@
 #include "ion/IonMacroAssembler.h"
 #include "ion/BaselineRegisters.h"
 #include "ion/BaselineIC.h"
 
 namespace js {
 namespace ion {
 
 inline void
+EmitRestoreTailCallReg(MacroAssembler &masm)
+{
+    masm.pop(BaselineTailCallReg);
+}
+
+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.movq(Operand(BaselineStubReg, (int32_t) ICEntry::offsetOfFirstStub()),
@@ -31,16 +37,22 @@ EmitCallIC(CodeOffsetLabel *patchOffset,
     masm.movq(Operand(BaselineStubReg, (int32_t) ICStub::offsetOfStubCode()),
               BaselineTailCallReg);
 
     // Call the stubcode.
     masm.call(BaselineTailCallReg);
 }
 
 inline void
+EmitReturnFromIC(MacroAssembler &masm)
+{
+    masm.ret();
+}
+
+inline void
 EmitTailCall(IonCode *target, MacroAssembler &masm, uint32_t argSize)
 {
     // We an assume during this that R0 and R1 have been pushed.
     masm.movq(BaselineFrameReg, ScratchReg);
     masm.addq(Imm32(BaselineFrame::FramePointerOffset), ScratchReg);
     masm.subq(BaselineStackReg, ScratchReg);
 
     // Store frame size without VMFunction arguments for GC marking.
--- a/js/src/ion/x64/BaselineIC-x64.cpp
+++ b/js/src/ion/x64/BaselineIC-x64.cpp
@@ -14,92 +14,86 @@
 using namespace js;
 using namespace js::ion;
 
 namespace js {
 namespace ion {
 
 // ICCompare_Int32
 
-IonCode *
-ICCompare_Int32::Compiler::generateStubCode()
+bool
+ICCompare_Int32::Compiler::generateStubCode(MacroAssembler &masm)
 {
     // The condition to test on depends on the opcode
     Assembler::Condition cond;
     switch(op) {
       case JSOP_LT: cond = Assembler::LessThan; break;
       case JSOP_GT: cond = Assembler::GreaterThan; break;
       default:
         JS_ASSERT(!"Unhandled op for ICCompare_Int32!");
-        return NULL;
+        return false;
     }
 
-    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);
 
     // Compare payload regs of R0 and R1.
     masm.unboxNonDouble(R0, rdx);
     masm.unboxNonDouble(R1, ScratchReg);
     masm.cmpq(rdx, ScratchReg);
     masm.setCC(cond, ScratchReg);
     masm.movzxbl(ScratchReg, ScratchReg);
 
     // Box the result and return
     masm.boxValue(JSVAL_TYPE_BOOLEAN, ScratchReg, R0.valueReg());
-    masm.ret();
+    EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
 
-    Linker linker(masm);
-    return linker.newCode(cx);
+    return true;
 }
 
 // ICBinaryArith_Int32
 
-IonCode *
-ICBinaryArith_Int32::Compiler::generateStubCode()
+bool
+ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler &masm)
 {
-    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 the TailCallReg which
     // should be available.
     masm.unboxNonDouble(R0, rdx);
     masm.unboxNonDouble(R1, ScratchReg);
 
     switch(op) {
       case JSOP_ADD:
         masm.addl(rdx, ScratchReg);
         break;
       default:
         JS_ASSERT(!"Unhandled op for BinaryArith_Int32!");
-        return NULL;
+        return false;
     }
 
     // Just jump to failure on overflow.  R0 and R1 are preserved, so we can just jump to
     // the next stub.
     masm.j(Assembler::Overflow, &failure);
 
     // Box the result and return
     masm.boxValue(JSVAL_TYPE_INT32, ScratchReg, R0.valueReg());
-    masm.ret();
+    EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
 
-    Linker linker(masm);
-    return linker.newCode(cx);
+    return true;
 }
 
 
 } // namespace ion
 } // namespace js
--- a/js/src/ion/x86/BaselineHelpers-x86.h
+++ b/js/src/ion/x86/BaselineHelpers-x86.h
@@ -11,16 +11,22 @@
 #include "ion/IonMacroAssembler.h"
 #include "ion/BaselineRegisters.h"
 #include "ion/BaselineIC.h"
 
 namespace js {
 namespace ion {
 
 inline void
+EmitRestoreTailCallReg(MacroAssembler &masm)
+{
+    masm.pop(BaselineTailCallReg);
+}
+
+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.movl(Operand(BaselineStubReg, (int32_t) ICEntry::offsetOfFirstStub()),
@@ -31,16 +37,22 @@ EmitCallIC(CodeOffsetLabel *patchOffset,
     masm.movl(Operand(BaselineStubReg, (int32_t) ICStub::offsetOfStubCode()),
               BaselineTailCallReg);
 
     // Call the stubcode.
     masm.call(BaselineTailCallReg);
 }
 
 inline void
+EmitReturnFromIC(MacroAssembler &masm)
+{
+    masm.ret();
+}
+
+inline void
 EmitTailCall(IonCode *target, MacroAssembler &masm, uint32_t argSize)
 {
     // We assume during this that R0 and R1 have been pushed.
 
     // Compute frame size.
     masm.movl(BaselineFrameReg, eax);
     masm.addl(Imm32(BaselineFrame::FramePointerOffset), eax);
     masm.subl(BaselineStackReg, eax);
--- a/js/src/ion/x86/BaselineIC-x86.cpp
+++ b/js/src/ion/x86/BaselineIC-x86.cpp
@@ -14,90 +14,84 @@
 using namespace js;
 using namespace js::ion;
 
 namespace js {
 namespace ion {
 
 // ICCompare_Int32
 
-IonCode *
-ICCompare_Int32::Compiler::generateStubCode()
+bool
+ICCompare_Int32::Compiler::generateStubCode(MacroAssembler &masm)
 {
     // The condition to test on depends on the opcode
     Assembler::Condition cond;
     switch(op) {
       case JSOP_LT: cond = Assembler::LessThan; break;
       case JSOP_GT: cond = Assembler::GreaterThan; break;
       default:
         JS_ASSERT(!"Unhandled op for ICCompare_Int32!");
-        return NULL;
+        return false;
     }
 
-    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);
 
     // Compare payload regs of R0 and R1.
     masm.cmpl(R0.payloadReg(), R1.payloadReg());
     masm.setCC(cond, R0.payloadReg());
     masm.movzxbl(R0.payloadReg(), R0.payloadReg());
 
     // Box the result and return
     masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0);
-    masm.ret();
+    EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
 
-    Linker linker(masm);
-    return linker.newCode(cx);
+    return true;
 }
 
 // ICBinaryArith_Int32
 
-IonCode *
-ICBinaryArith_Int32::Compiler::generateStubCode()
+bool
+ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler &masm)
 {
-    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 the TailCallReg which
     // should be available.
     Register scratchReg = BaselineTailCallReg;
 
     switch(op) {
       case JSOP_ADD:
         masm.movl(R1.payloadReg(), scratchReg);
         masm.addl(R0.payloadReg(), scratchReg);
         break;
       default:
         JS_ASSERT(!"Unhandled op for BinaryArith_Int32!");
-        return NULL;
+        return false;
     }
 
     // Just jump to failure on overflow.  R0 and R1 are preserved, so we can just jump to
     // the next stub.
     masm.j(Assembler::Overflow, &failure);
 
     // Box the result and return
     masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
-    masm.ret();
+    EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
 
-    Linker linker(masm);
-    return linker.newCode(cx);
+    return true;
 }
 
 
 } // namespace ion
 } // namespace js