Bug 1112160 - Baseline Stubs: Align Jit frames before calling any jitted code. r=jandem
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Thu, 12 Feb 2015 14:53:06 +0100
changeset 228832 2eca2e88f8e319adaec6955b6337f95f479bf48c
parent 228831 b7154cb4aad26776e511a9195f48d5644a2a9211
child 228833 25c0f57abbdb02343609db99e72339a405841c4e
push id28272
push userryanvm@gmail.com
push dateThu, 12 Feb 2015 22:53:38 +0000
treeherdermozilla-central@f969dd9c7a05 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1112160
milestone38.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 1112160 - Baseline Stubs: Align Jit frames before calling any jitted code. r=jandem
js/src/jit-test/tests/ion/stack-alignment.js
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/JitFrames.cpp
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
--- a/js/src/jit-test/tests/ion/stack-alignment.js
+++ b/js/src/jit-test/tests/ion/stack-alignment.js
@@ -48,24 +48,71 @@ function ionFrameSize_callee_verify(a, b
 
 var ionFrameSize_args = [];
 for (var l = 0; l < 4; l++) {
   ionFrameSize_args[l] = [];
   for (var a = 0; a < 4; a++)
     ionFrameSize_args[l][a] = gen_ionFrameSize(30 + l, a, "ionFrameSize_callee_verify");;
 }
 
-// Check ion frames during function calls are always correctly aligned.
+// Check ion frames during function apply calls with the argument vector.
 function ionFrame_funApply_0() {
   assertJitStackInvariants.apply(this, arguments);
 }
 function ionFrame_funApply_1() {
   ionFrame_funApply_0.apply(this, arguments);
 }
 
+// Check ion frames during function apply calls with an array of arguments.
+function ionFrame_funApply_2() {
+  var arr = Array.apply(Array, arguments);
+  assertJitStackInvariants.apply(this, arr);
+}
+function ionFrame_funApply_3() {
+  var arr = Array.apply(Array, arguments);
+  ionFrame_funApply_2.apply(this, arr);
+}
+
+// Check ion frames during function .call calls.
+function ionFrame_funCall_0() {
+  assertJitStackInvariants.call(this);
+}
+function ionFrame_funCall_1(a) {
+  assertJitStackInvariants.call(this, a);
+}
+function ionFrame_funCall_2(a, b) {
+  assertJitStackInvariants.call(this, a, b);
+}
+function ionFrame_funCall_3(a, b, c) {
+  assertJitStackInvariants.call(this, a, b, c);
+}
+
+function ionFrame_funCall_x0() {
+  ionFrame_funCall_0.call(this);
+}
+function ionFrame_funCall_x1(a) {
+  ionFrame_funCall_1.call(this, a);
+}
+function ionFrame_funCall_x2(a, b) {
+  ionFrame_funCall_2.call(this, a, b);
+}
+function ionFrame_funCall_x3(a, b, c) {
+  ionFrame_funCall_3.call(this, a, b, c);
+}
+
+// Check ion frames during spread calls.
+function ionFrame_spreadCall_0() {
+  var arr = Array.apply(Array, arguments);
+  assertJitStackInvariants(...arr);
+}
+function ionFrame_spreadCall_1() {
+  var arr = Array.apply(Array, arguments);
+  ionFrame_spreadCall_0(...arr);
+}
+
 
 for (i = 0; i < 40; i++) {
   entryFrame_1();
   entryFrame_1(0);
   entryFrame_1(0, 1);
 
   rectifierFrame_1(i);
   rectifierFrame_2(i);
@@ -84,9 +131,36 @@ for (i = 0; i < 40; i++) {
   ionFrame_funApply_0();
   ionFrame_funApply_0(1);
   ionFrame_funApply_0(1, 2);
   ionFrame_funApply_0(1, 2, 3);
   ionFrame_funApply_1();
   ionFrame_funApply_1(1);
   ionFrame_funApply_1(1, 2);
   ionFrame_funApply_1(1, 2, 3);
+
+  ionFrame_funApply_2();
+  ionFrame_funApply_2(1);
+  ionFrame_funApply_2(1, 2);
+  ionFrame_funApply_2(1, 2, 3);
+  ionFrame_funApply_3();
+  ionFrame_funApply_3(1);
+  ionFrame_funApply_3(1, 2);
+  ionFrame_funApply_3(1, 2, 3);
+
+  ionFrame_funCall_0();
+  ionFrame_funCall_1(1);
+  ionFrame_funCall_2(1, 2);
+  ionFrame_funCall_3(1, 2, 3);
+  ionFrame_funCall_x0();
+  ionFrame_funCall_x1(1);
+  ionFrame_funCall_x2(1, 2);
+  ionFrame_funCall_x3(1, 2, 3);
+
+  ionFrame_spreadCall_0();
+  ionFrame_spreadCall_0(1);
+  ionFrame_spreadCall_0(1, 2);
+  ionFrame_spreadCall_0(1, 2, 3);
+  ionFrame_spreadCall_1();
+  ionFrame_spreadCall_1(1);
+  ionFrame_spreadCall_1(1, 2);
+  ionFrame_spreadCall_1(1, 2, 3);
 }
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -9617,33 +9617,39 @@ DoSpreadCallFallback(JSContext *cx, Base
     // Add a type monitor stub for the resulting value.
     if (!stub->addMonitorStubForValue(cx, script, res))
         return false;
 
     return true;
 }
 
 void
-ICCallStubCompiler::pushCallArguments(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg)
+ICCallStubCompiler::pushCallArguments(MacroAssembler &masm, GeneralRegisterSet regs,
+                                      Register argcReg, bool isJitCall)
 {
     MOZ_ASSERT(!regs.has(argcReg));
 
     // Push the callee and |this| too.
     Register count = regs.takeAny();
     masm.mov(argcReg, count);
     masm.add32(Imm32(2), count);
 
     // argPtr initially points to the last argument.
     Register argPtr = regs.takeAny();
     masm.mov(BaselineStackReg, argPtr);
 
     // Skip 4 pointers pushed on top of the arguments: the frame descriptor,
     // return address, old frame pointer and stub reg.
     masm.addPtr(Imm32(STUB_FRAME_SIZE), argPtr);
 
+    // Align the stack such that the JitFrameLayout is aligned on the
+    // JitStackAlignment.
+    if (isJitCall)
+        masm.alignJitStackBasedOnNArgs(argcReg);
+
     // Push all values, starting at the last one.
     Label loop, done;
     masm.bind(&loop);
     masm.branchTest32(Assembler::Zero, count, count, &done);
     {
         masm.pushValue(Address(argPtr, 0));
         masm.addPtr(Imm32(sizeof(Value)), argPtr);
 
@@ -9665,28 +9671,33 @@ ICCallStubCompiler::guardSpreadCall(Macr
     static_assert(ICCall_Scripted::MAX_ARGS_SPREAD_LENGTH <= ARGS_LENGTH_MAX,
                   "maximum arguments length for optimized stub should be <= ARGS_LENGTH_MAX");
     masm.branch32(Assembler::Above, argcReg, Imm32(ICCall_Scripted::MAX_ARGS_SPREAD_LENGTH),
                   failure);
 }
 
 void
 ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler &masm, GeneralRegisterSet regs,
-                                            Register argcReg)
+                                            Register argcReg, bool isJitCall)
 {
     // Push arguments
     Register startReg = regs.takeAny();
     Register endReg = regs.takeAny();
     masm.unboxObject(Address(BaselineStackReg, STUB_FRAME_SIZE), startReg);
     masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg);
     masm.mov(argcReg, endReg);
     static_assert(sizeof(Value) == 8, "Value must be 8 bytes");
     masm.lshiftPtr(Imm32(3), endReg);
     masm.addPtr(startReg, endReg);
 
+    // Align the stack such that the JitFrameLayout is aligned on the
+    // JitStackAlignment.
+    if (isJitCall)
+        masm.alignJitStackBasedOnNArgs(argcReg);
+
     // Copying pre-decrements endReg by 8 until startReg is reached
     Label copyDone;
     Label copyStart;
     masm.bind(&copyStart);
     masm.branchPtr(Assembler::Equal, endReg, startReg, &copyDone);
     masm.subPtr(Imm32(sizeof(Value)), endReg);
     masm.pushValue(Address(endReg, 0));
     masm.jump(&copyStart);
@@ -9819,16 +9830,17 @@ ICCallStubCompiler::pushCallerArguments(
 {
     // Initialize copyReg to point to start caller arguments vector.
     // Initialize argcReg to poitn to the end of it.
     Register startReg = regs.takeAny();
     Register endReg = regs.takeAny();
     masm.loadPtr(Address(BaselineFrameReg, 0), startReg);
     masm.loadPtr(Address(startReg, BaselineFrame::offsetOfNumActualArgs()), endReg);
     masm.addPtr(Imm32(BaselineFrame::offsetOfArg(0)), startReg);
+    masm.alignJitStackBasedOnNArgs(endReg);
     masm.lshiftPtr(Imm32(ValueShift), endReg);
     masm.addPtr(startReg, endReg);
 
     // Copying pre-decrements endReg by 8 until startReg is reached
     Label copyDone;
     Label copyStart;
     masm.bind(&copyStart);
     masm.branchPtr(Assembler::Equal, endReg, startReg, &copyDone);
@@ -9845,16 +9857,17 @@ ICCallStubCompiler::pushArrayArguments(M
     // Load start and end address of values to copy.
     // guardFunApply has already gauranteed that the array is packed and contains
     // no holes.
     Register startReg = regs.takeAny();
     Register endReg = regs.takeAny();
     masm.extractObject(arrayVal, startReg);
     masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg);
     masm.load32(Address(startReg, ObjectElements::offsetOfInitializedLength()), endReg);
+    masm.alignJitStackBasedOnNArgs(endReg);
     masm.lshiftPtr(Imm32(ValueShift), endReg);
     masm.addPtr(startReg, endReg);
 
     // Copying pre-decrements endReg by 8 until startReg is reached
     Label copyDone;
     Label copyStart;
     masm.bind(&copyStart);
     masm.branchPtr(Assembler::Equal, endReg, startReg, &copyDone);
@@ -9909,17 +9922,17 @@ ICCall_Fallback::Compiler::generateStubC
 
         // SPREADCALL is not yet supported in Ion, so do not generate asmcode for
         // bailout.
         return true;
     }
 
     regs.take(R0.scratchReg()); // argc.
 
-    pushCallArguments(masm, regs, R0.scratchReg());
+    pushCallArguments(masm, regs, R0.scratchReg(), /* isJitCall = */ false);
 
     masm.push(BaselineStackReg);
     masm.push(R0.scratchReg());
     masm.push(BaselineStubReg);
 
     // Load previous frame pointer, push BaselineFrame *.
     masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg());
     masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg());
@@ -10139,19 +10152,19 @@ ICCallScriptedCompiler::generateStubCode
             regs.addUnchecked(BaselineTailCallReg);
     }
     Register scratch = regs.takeAny();
 
     // 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.
     if (isSpread_)
-        pushSpreadCallArguments(masm, regs, argcReg);
+        pushSpreadCallArguments(masm, regs, argcReg, /* isJitCall = */ true);
     else
-        pushCallArguments(masm, regs, argcReg);
+        pushCallArguments(masm, regs, argcReg, /* isJitCall = */ true);
 
     // The callee is on top of the stack. Pop and unbox it.
     ValueOperand val = regs.takeAnyValue();
     masm.popValue(val);
     callee = masm.extractObject(val, ExtractTemp0);
 
     EmitCreateStubFrameDescriptor(masm, scratch);
 
@@ -10182,37 +10195,50 @@ ICCallScriptedCompiler::generateStubCode
     masm.callJit(code);
 
     // If this is a constructing call, and the callee returns a non-object, replace it with
     // the |this| object passed in.
     if (isConstructing_) {
         Label skipThisReplace;
         masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
 
-        Register scratchReg = JSReturnOperand.scratchReg();
-
-        // Current stack: [ ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
+        // Current stack: [ Padding?, ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
         // However, we can't use this ThisVal, because it hasn't been traced.  We need to use
         // The ThisVal higher up the stack:
         // Current stack: [ ThisVal, ARGVALS..., ...STUB FRAME...,
-        //                  ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
-        masm.loadPtr(Address(BaselineStackReg, 2*sizeof(size_t)), scratchReg);
-
-        // scratchReg now contains actualArgCount.  Double it to account for skipping past two
-        // pushed copies of argument values for non-spread, increment it to account for skipping
-        // actual argument values and array object for spread.  Additionally, we need to add:
-        // STUB_FRAME_SIZE + sizeof(ThisVal) + sizeof(size_t) + sizeof(void *) + sizoef(size_t)
-        // for: stub frame, this value, actual argc, callee, and descriptor
-        if (isSpread_)
-            masm.add32(Imm32(1), scratchReg);
-        else
-            masm.lshiftPtr(Imm32(1), scratchReg);
-        BaseValueIndex reloadThisSlot(BaselineStackReg, scratchReg,
-                                      STUB_FRAME_SIZE + sizeof(Value) + 3 * sizeof(size_t));
-        masm.loadValue(reloadThisSlot, JSReturnOperand);
+        //                  Padding?, ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
+
+        // Restore the BaselineFrameReg based on the frame descriptor.
+        //
+        // BaselineFrameReg = BaselineStackReg
+        //                  + sizeof(Descriptor) + sizeof(Callee) + sizeof(ActualArgc)
+        //                  + stubFrameSize(Descriptor)
+        //                  - sizeof(BaselineStubReg) - sizeof(BaselineFrameReg)
+        Address descriptorAddr(BaselineStackReg, 0);
+        masm.loadPtr(descriptorAddr, BaselineFrameReg);
+        masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), BaselineFrameReg);
+        masm.addPtr(Imm32((3 - 2) * sizeof(size_t)), BaselineFrameReg);
+        masm.addPtr(BaselineStackReg, BaselineFrameReg);
+
+        // Load the number of arguments present before the stub frame.
+        Register argcReg = JSReturnOperand.scratchReg();
+        if (isSpread_) {
+            // Account for the Array object.
+            masm.move32(Imm32(1), argcReg);
+        } else {
+            Address argcAddr(BaselineStackReg, 2 * sizeof(size_t));
+            masm.loadPtr(argcAddr, argcReg);
+        }
+
+        // Current stack: [ ThisVal, ARGVALS..., ...STUB FRAME..., <-- BaselineFrameReg
+        //                  Padding?, ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
+        //
+        // &ThisVal = BaselineFrameReg + argc * sizeof(Value) + STUB_FRAME_SIZE
+        BaseValueIndex thisSlotAddr(BaselineFrameReg, argcReg, STUB_FRAME_SIZE);
+        masm.loadValue(thisSlotAddr, JSReturnOperand);
 #ifdef DEBUG
         masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
         masm.assumeUnreachable("Return of constructing call should be an object.");
 #endif
         masm.bind(&skipThisReplace);
     }
 
     leaveStubFrame(masm, true);
@@ -10402,19 +10428,19 @@ ICCall_Native::Compiler::generateStubCod
     // Push a stub frame so that we can perform a non-tail call.
     // Note that this leaves the return address in TailCallReg.
     enterStubFrame(masm, regs.getAny());
 
     // 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.
     if (isSpread_)
-        pushSpreadCallArguments(masm, regs, argcReg);
+        pushSpreadCallArguments(masm, regs, argcReg, /* isJitCall = */ false);
     else
-        pushCallArguments(masm, regs, argcReg);
+        pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false);
 
     if (isConstructing_) {
         // Stack looks like: [ ..., Arg0Val, ThisVal, CalleeVal ]
         // Replace ThisVal with MagicValue(JS_IS_CONSTRUCTING)
         masm.storeValue(MagicValue(JS_IS_CONSTRUCTING), Address(BaselineStackReg, sizeof(Value)));
     }
 
     masm.checkStackAlignment();
@@ -10499,17 +10525,17 @@ ICCall_ClassHook::Compiler::generateStub
     regs.add(R1);
     regs.takeUnchecked(callee);
 
     // Push a stub frame so that we can perform a non-tail call.
     // Note that this leaves the return address in TailCallReg.
     enterStubFrame(masm, regs.getAny());
 
     regs.add(scratch);
-    pushCallArguments(masm, regs, argcReg);
+    pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false);
     regs.take(scratch);
 
     if (isConstructing_) {
         // Stack looks like: [ ..., Arg0Val, ThisVal, CalleeVal ]
         // Replace ThisVal with MagicValue(JS_IS_CONSTRUCTING)
         masm.storeValue(MagicValue(JS_IS_CONSTRUCTING), Address(BaselineStackReg, sizeof(Value)));
     }
 
@@ -10806,17 +10832,17 @@ ICCall_ScriptedFunCall::Compiler::genera
 
     // Push a stub frame so that we can perform a non-tail call.
     enterStubFrame(masm, regs.getAny());
     if (canUseTailCallReg)
         regs.add(BaselineTailCallReg);
 
     // Values are on the stack left-to-right. Calling convention wants them
     // right-to-left so duplicate them on the stack in reverse order.
-    pushCallArguments(masm, regs, argcReg);
+    pushCallArguments(masm, regs, argcReg, /* isJitCall = */ true);
 
     // Discard callee (function.call).
     masm.addPtr(Imm32(sizeof(Value)), StackPointer);
 
     // Pop scripted callee (the original |this|).
     ValueOperand val = regs.takeAnyValue();
     masm.popValue(val);
 
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -5903,18 +5903,20 @@ class ICCallStubCompiler : public ICStub
       : ICStubCompiler(cx, kind)
     { }
 
     enum FunApplyThing {
         FunApply_MagicArgs,
         FunApply_Array
     };
 
-    void pushCallArguments(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg);
-    void pushSpreadCallArguments(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg);
+    void pushCallArguments(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg,
+                           bool isJitCall);
+    void pushSpreadCallArguments(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg,
+                                 bool isJitCall);
     void guardSpreadCall(MacroAssembler &masm, Register argcReg, Label *failure);
     Register guardFunApply(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg,
                            bool checkNative, FunApplyThing applyThing, Label *failure);
     void pushCallerArguments(MacroAssembler &masm, GeneralRegisterSet regs);
     void pushArrayArguments(MacroAssembler &masm, Address arrayVal, GeneralRegisterSet regs);
 };
 
 class ICCall_Fallback : public ICMonitoredFallbackStub
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -3084,16 +3084,17 @@ InvalidationBailoutStack::checkInvariant
 
 void
 AssertJitStackInvariants(JSContext *cx)
 {
     for (JitActivationIterator activations(cx->runtime()); !activations.done(); ++activations) {
         JitFrameIterator frames(activations);
         size_t prevFrameSize = 0;
         size_t frameSize = 0;
+        bool isScriptedCallee = false;
         for (; !frames.done(); ++frames) {
             size_t calleeFp = reinterpret_cast<size_t>(frames.fp());
             size_t callerFp = reinterpret_cast<size_t>(frames.prevFp());
             MOZ_ASSERT(callerFp >= calleeFp);
             prevFrameSize = frameSize;
             frameSize = callerFp - calleeFp;
 
             if (frames.prevType() == JitFrame_Rectifier) {
@@ -3129,16 +3130,27 @@ AssertJitStackInvariants(JSContext *cx)
 
                 InlineFrameIterator lastInlinedFrame(cx, &frames);
                 jsbytecode *pc = lastInlinedFrame.pc();
                 if (JSOp(*pc) == JSOP_FUNAPPLY) {
                     MOZ_RELEASE_ASSERT(prevFrameSize % JitStackAlignment == 0,
                       "The ion frame should keep the alignment");
                 }
             }
+
+            // The stack is dynamically aligned by baseline stubs before calling
+            // any jitted code.
+            if (frames.prevType() == JitFrame_BaselineStub && isScriptedCallee) {
+                MOZ_RELEASE_ASSERT(calleeFp % JitStackAlignment == 0,
+                    "The baseline stub restores the stack alignment");
+            }
+
+            isScriptedCallee = false
+                || frames.isScripted()
+                || frames.type() == JitFrame_Rectifier;
         }
 
         MOZ_RELEASE_ASSERT(frames.type() == JitFrame_Entry,
           "The first frame of a Jit activation should be an entry frame");
         MOZ_RELEASE_ASSERT(reinterpret_cast<size_t>(frames.fp()) % JitStackAlignment == 0,
           "The entry frame should be properly aligned");
     }
 }
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -2291,8 +2291,56 @@ MacroAssembler::profilerPreCallImpl(Regi
     AbsoluteAddress profilingActivation(icx->runtime->addressOfProfilingActivation());
 
     CodeOffsetLabel label = movWithPatch(ImmWord(uintptr_t(-1)), reg);
     loadPtr(profilingActivation, reg2);
     storePtr(reg, Address(reg2, JitActivation::offsetOfLastProfilingCallSite()));
 
     appendProfilerCallSite(label);
 }
+
+void
+MacroAssembler::alignJitStackBasedOnNArgs(Register nargs)
+{
+    const uint32_t alignment = JitStackAlignment / sizeof(Value);
+    if (alignment == 1)
+        return;
+
+    // A JitFrameLayout is composed of the following:
+    // [padding?] [argN] .. [arg1] [this] [[argc] [callee] [descr] [raddr]]
+    //
+    // We want to ensure that the |raddr| address is aligned.
+    // Which implies that we want to ensure that |this| is aligned.
+    static_assert(sizeof(JitFrameLayout) % JitStackAlignment == 0,
+      "No need to consider the JitFrameLayout for aligning the stack");
+
+    // Which implies that |argN| is aligned if |nargs| is even, and offset by
+    // |sizeof(Value)| if |nargs| is odd.
+    MOZ_ASSERT(alignment == 2);
+
+    // Thus the |padding| is offset by |sizeof(Value)| if |nargs| is even, and
+    // aligned if |nargs| is odd.
+
+    // if (nargs % 2 == 0) {
+    //     if (sp % JitStackAlignment == 0)
+    //         sp -= sizeof(Value);
+    //     MOZ_ASSERT(sp % JitStackAlignment == JitStackAlignment - sizeof(Value));
+    // } else {
+    //     sp = sp & ~(JitStackAlignment - 1);
+    // }
+    Label odd, end;
+    Label *maybeAssert = &end;
+#ifdef DEBUG
+    Label assert;
+    maybeAssert = &assert;
+#endif
+    branchTestPtr(Assembler::NonZero, nargs, Imm32(1), &odd);
+    branchTestPtr(Assembler::NonZero, StackPointer, Imm32(JitStackAlignment - 1), maybeAssert);
+    subPtr(Imm32(sizeof(Value)), StackPointer);
+#ifdef DEBUG
+    bind(&assert);
+#endif
+    assertStackAlignment(JitStackAlignment, sizeof(Value));
+    jump(&end);
+    bind(&odd);
+    andPtr(Imm32(~(JitStackAlignment - 1)), StackPointer);
+    bind(&end);
+}
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1247,16 +1247,21 @@ class MacroAssembler : public MacroAssem
     }
 
     void icRestoreLive(RegisterSet &liveRegs, AfterICSaveLive &aic) {
         restoreFrameAlignmentForICArguments(aic);
         MOZ_ASSERT(framePushed() == aic.initialStack);
         PopRegsInMask(liveRegs);
     }
 
+    // Align the stack pointer based on the number of arguments which are pushed
+    // on the stack, such that the JitFrameLayout would be correctly aligned on
+    // the JitStackAlignment.
+    void alignJitStackBasedOnNArgs(Register nargs);
+
     void assertStackAlignment(uint32_t alignment, int32_t offset = 0) {
 #ifdef DEBUG
         Label ok, bad;
         MOZ_ASSERT(IsPowerOfTwo(alignment));
 
         // Wrap around the offset to be a non-negative number.
         offset %= alignment;
         if (offset < 0)