Title: [187618] branches/jsc-tailcall/Source/_javascript_Core
Revision
187618
Author
basile_clem...@apple.com
Date
2015-07-30 16:19:30 -0700 (Thu, 30 Jul 2015)

Log Message

jsc-tailcall: Don't waste stack space when arity fixup was performed
https://bugs.webkit.org/show_bug.cgi?id=147447

Reviewed by Michael Saboff.

When doing a tail call, we overwrite an amount of stack space based on
the number of arguments in the call frame. If we entered the tail
caller by performing an arity fixup, this is incorrect and leads to
wasted stack space - we must use the CodeBlock's number of parameters
instead in that case.

This patch is also moving the prepareForTailCall() function from
jit/ThunkGenerators.h to the place where it should have always been,
namely jit/CCallHelpers.h

* jit/CCallHelpers.h:
(JSC::CCallHelpers::prepareForTailCallSlow):
* jit/JITCall.cpp:
(JSC::JIT::compileOpCall):
* jit/Repatch.cpp:
(JSC::linkPolymorphicCall):
* jit/ThunkGenerators.cpp:
(JSC::slowPathFor):
(JSC::virtualThunkFor):
* jit/ThunkGenerators.h:
* tests/stress/tail-call-no-stack-overflow.js:
(strictLoopArityFixup):

Modified Paths

Diff

Modified: branches/jsc-tailcall/Source/_javascript_Core/ChangeLog (187617 => 187618)


--- branches/jsc-tailcall/Source/_javascript_Core/ChangeLog	2015-07-30 23:17:45 UTC (rev 187617)
+++ branches/jsc-tailcall/Source/_javascript_Core/ChangeLog	2015-07-30 23:19:30 UTC (rev 187618)
@@ -1,5 +1,35 @@
 2015-07-30  Basile Clement  <basile_clem...@apple.com>
 
+        jsc-tailcall: Don't waste stack space when arity fixup was performed
+        https://bugs.webkit.org/show_bug.cgi?id=147447
+
+        Reviewed by Michael Saboff.
+
+        When doing a tail call, we overwrite an amount of stack space based on
+        the number of arguments in the call frame. If we entered the tail
+        caller by performing an arity fixup, this is incorrect and leads to
+        wasted stack space - we must use the CodeBlock's number of parameters
+        instead in that case.
+
+        This patch is also moving the prepareForTailCall() function from
+        jit/ThunkGenerators.h to the place where it should have always been,
+        namely jit/CCallHelpers.h
+
+        * jit/CCallHelpers.h:
+        (JSC::CCallHelpers::prepareForTailCallSlow):
+        * jit/JITCall.cpp:
+        (JSC::JIT::compileOpCall):
+        * jit/Repatch.cpp:
+        (JSC::linkPolymorphicCall):
+        * jit/ThunkGenerators.cpp:
+        (JSC::slowPathFor):
+        (JSC::virtualThunkFor):
+        * jit/ThunkGenerators.h:
+        * tests/stress/tail-call-no-stack-overflow.js:
+        (strictLoopArityFixup):
+
+2015-07-30  Basile Clement  <basile_clem...@apple.com>
+
         jsc-tailcall: We should consider a tail call as an exit in the LLInt for the purpose of switching to the JIT
         https://bugs.webkit.org/show_bug.cgi?id=147449
 

Modified: branches/jsc-tailcall/Source/_javascript_Core/jit/CCallHelpers.h (187617 => 187618)


--- branches/jsc-tailcall/Source/_javascript_Core/jit/CCallHelpers.h	2015-07-30 23:17:45 UTC (rev 187617)
+++ branches/jsc-tailcall/Source/_javascript_Core/jit/CCallHelpers.h	2015-07-30 23:19:30 UTC (rev 187618)
@@ -30,6 +30,7 @@
 
 #include "AssemblyHelpers.h"
 #include "GPRInfo.h"
+#include "StackAlignment.h"
 
 namespace JSC {
 
@@ -2020,6 +2021,70 @@
         loadPtr(&vm()->targetMachinePCForThrow, GPRInfo::regT1);
         jump(GPRInfo::regT1);
     }
+
+    void prepareForTailCallSlow(const TempRegisterSet& usedRegisters = { RegisterSet::specialRegisters() })
+    {
+        GPRReg temp1 = usedRegisters.getFreeGPR(0);
+        GPRReg temp2 = usedRegisters.getFreeGPR(1);
+        ASSERT(temp2 != InvalidGPRReg);
+
+        subPtr(TrustedImm32(sizeof(CallerFrameAndPC)), stackPointerRegister);
+        loadPtr(Address(GPRInfo::callFrameRegister), temp1);
+        storePtr(temp1, Address(stackPointerRegister));
+        loadPtr(Address(GPRInfo::callFrameRegister, sizeof(void*)), temp1);
+        storePtr(temp1, Address(stackPointerRegister, sizeof(void*)));
+
+        // Now stackPointerRegister points to a valid call frame for the callee
+        // and callFrameRegister points to our own call frame.
+        // We now slide the callee's call frame over our own call frame,
+        // starting with the top to avoid unwanted overwrites
+
+        // Move the callFrameRegister to the top of our (trashed) call frame
+        load32(Address(GPRInfo::callFrameRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset),
+            temp1);
+
+        // We need to use the number of parameters instead of the argument count in case of arity fixup
+        loadPtr(Address(GPRInfo::callFrameRegister, JSStack::CodeBlock * static_cast<int>(sizeof(Register))), temp2);
+        load32(Address(temp2, CodeBlock::offsetOfNumParameters()), temp2);
+        MacroAssembler::Jump argumentCountWasNotFixedUp = branch32(BelowOrEqual, temp2, temp1);
+        move(temp2, temp1);
+        argumentCountWasNotFixedUp.link(this);
+
+        add32(TrustedImm32(stackAlignmentRegisters() + JSStack::CallFrameHeaderSize - 1), temp1);
+        and32(TrustedImm32(-stackAlignmentRegisters()), temp1);
+        mul32(TrustedImm32(sizeof(Register)), temp1, temp1);
+        addPtr(temp1, GPRInfo::callFrameRegister);
+
+        // Compute the call frame size of the callee's call frame
+        load32(Address(stackPointerRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset),
+            temp2);
+        add32(TrustedImm32(stackAlignmentRegisters() + JSStack::CallFrameHeaderSize - 1), temp2);
+        and32(TrustedImm32(-stackAlignmentRegisters()), temp2);
+
+#if USE(JSVALUE32_64)
+        COMPILE_ASSERT(sizeof(void*) * 2 == sizeof(Register), Register_is_two_pointers_sized);
+        lshift32(TrustedImm32(1), temp2);
+#endif
+
+        // Do the sliding
+        MacroAssembler::Label copyLoop(label());
+
+        subPtr(TrustedImm32(sizeof(void*)), GPRInfo::callFrameRegister);
+        sub32(TrustedImm32(1), temp2);
+        loadPtr(BaseIndex(stackPointerRegister, temp2, MacroAssembler::timesPtr()), temp1);
+        storePtr(temp1, Address(GPRInfo::callFrameRegister));
+
+        branchTest32(MacroAssembler::NonZero, temp2).linkTo(copyLoop, this);
+
+        emitFunctionEpilogue();
+    }
+
+    void prepareForTailCallSlow(GPRReg calleeGPR)
+    {
+        TempRegisterSet usedRegisters { RegisterSet::specialRegisters() };
+        usedRegisters.set(calleeGPR);
+        prepareForTailCallSlow(usedRegisters);
+    }
 };
 
 } // namespace JSC

Modified: branches/jsc-tailcall/Source/_javascript_Core/jit/JITCall.cpp (187617 => 187618)


--- branches/jsc-tailcall/Source/_javascript_Core/jit/JITCall.cpp	2015-07-30 23:17:45 UTC (rev 187617)
+++ branches/jsc-tailcall/Source/_javascript_Core/jit/JITCall.cpp	2015-07-30 23:19:30 UTC (rev 187618)
@@ -192,7 +192,7 @@
     m_callCompilationInfo[callLinkInfoIndex].callLinkInfo = info;
 
     if (opcodeID == op_tail_call || opcodeID == op_tail_call_varargs) {
-        prepareForTailCall(*this);
+        prepareForTailCallSlow();
         m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedTailCall();
         // We must never come back here
         breakpoint();

Modified: branches/jsc-tailcall/Source/_javascript_Core/jit/JITCall32_64.cpp (187617 => 187618)


--- branches/jsc-tailcall/Source/_javascript_Core/jit/JITCall32_64.cpp	2015-07-30 23:17:45 UTC (rev 187617)
+++ branches/jsc-tailcall/Source/_javascript_Core/jit/JITCall32_64.cpp	2015-07-30 23:19:30 UTC (rev 187618)
@@ -276,7 +276,7 @@
 
     checkStackPointerAlignment();
     if (opcodeID == op_tail_call || opcodeID == op_tail_call_varargs) {
-        prepareForTailCall(*this);
+        prepareForTailCallSlow();
         m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedTailCall();
         // We must never come back here
         breakpoint();

Modified: branches/jsc-tailcall/Source/_javascript_Core/jit/Repatch.cpp (187617 => 187618)


--- branches/jsc-tailcall/Source/_javascript_Core/jit/Repatch.cpp	2015-07-30 23:17:45 UTC (rev 187617)
+++ branches/jsc-tailcall/Source/_javascript_Core/jit/Repatch.cpp	2015-07-30 23:19:30 UTC (rev 187618)
@@ -1881,7 +1881,7 @@
                 CCallHelpers::Address(fastCountsBaseGPR, caseIndex * sizeof(uint32_t)));
         }
         if (CallLinkInfo::isTailCallType(callLinkInfo.callType())) {
-            prepareForTailCall(stubJit);
+            stubJit.prepareForTailCallSlow();
             calls[caseIndex].call = stubJit.nearTailCall();
         } else
             calls[caseIndex].call = stubJit.nearCall();

Modified: branches/jsc-tailcall/Source/_javascript_Core/jit/ThunkGenerators.cpp (187617 => 187618)


--- branches/jsc-tailcall/Source/_javascript_Core/jit/ThunkGenerators.cpp	2015-07-30 23:17:45 UTC (rev 187617)
+++ branches/jsc-tailcall/Source/_javascript_Core/jit/ThunkGenerators.cpp	2015-07-30 23:19:30 UTC (rev 187618)
@@ -102,9 +102,7 @@
     CCallHelpers::Jump doNotTrash = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::returnValueGPR2);
 
     jit.preserveReturnAddressAfterCall(GPRInfo::nonPreservedNonReturnGPR);
-    jit.move(GPRInfo::returnValueGPR, GPRInfo::regT4); // FIXME
-    prepareForTailCall(jit);
-    jit.jump(GPRInfo::regT4);
+    jit.prepareForTailCallSlow(GPRInfo::returnValueGPR);
 
     doNotTrash.link(&jit);
     jit.jump(GPRInfo::returnValueGPR);
@@ -195,7 +193,7 @@
     emitPointerValidation(jit, GPRInfo::regT4);
     if (CallLinkInfo::isTailCallType(callLinkInfo.callType())) {
         jit.preserveReturnAddressAfterCall(GPRInfo::regT0);
-        prepareForTailCall(jit);
+        jit.prepareForTailCallSlow(GPRInfo::regT4);
     }
     jit.jump(GPRInfo::regT4);
 
@@ -461,51 +459,6 @@
     return FINALIZE_CODE(patchBuffer, ("unreachable thunk"));
 }
 
-void prepareForTailCall(CCallHelpers& jit)
-{
-    jit.subPtr(CCallHelpers::TrustedImm32(sizeof(CallerFrameAndPC)), JSInterfaceJIT::stackPointerRegister);
-    jit.loadPtr(CCallHelpers::Address(JSInterfaceJIT::callFrameRegister), JSInterfaceJIT::regT1);
-    jit.storePtr(JSInterfaceJIT::regT1, CCallHelpers::Address(JSInterfaceJIT::stackPointerRegister));
-    jit.loadPtr(CCallHelpers::Address(JSInterfaceJIT::callFrameRegister, sizeof(void*)), JSInterfaceJIT::regT1);
-    jit.storePtr(JSInterfaceJIT::regT1, CCallHelpers::Address(JSInterfaceJIT::stackPointerRegister, sizeof(void*)));
-
-    // Now stackPointerRegister points to a valid call frame for the callee
-    // and callFrameRegister points to our own call frame.
-    // We now slide the callee's call frame over our own call frame,
-    // starting with the top to avoid unwanted overwrites
-
-    // Move the callFrameRegister to the top of our (trashed) call frame
-    jit.load32(CCallHelpers::Address(JSInterfaceJIT::callFrameRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset),
-        JSInterfaceJIT::regT1);
-    jit.add32(CCallHelpers::TrustedImm32(stackAlignmentRegisters() + JSStack::CallFrameHeaderSize - 1), JSInterfaceJIT::regT1);
-    jit.and32(CCallHelpers::TrustedImm32(-stackAlignmentRegisters()), JSInterfaceJIT::regT1);
-    jit.mul32(CCallHelpers::TrustedImm32(sizeof(Register)), JSInterfaceJIT::regT1, JSInterfaceJIT::regT1);
-    jit.addPtr(JSInterfaceJIT::regT1, JSInterfaceJIT::callFrameRegister);
-
-    // Compute the call frame size of the callee's call frame
-    jit.load32(CCallHelpers::Address(JSInterfaceJIT::stackPointerRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset),
-        JSInterfaceJIT::regT2);
-    jit.add32(CCallHelpers::TrustedImm32(stackAlignmentRegisters() + JSStack::CallFrameHeaderSize - 1), JSInterfaceJIT::regT2);
-    jit.and32(CCallHelpers::TrustedImm32(-stackAlignmentRegisters()), JSInterfaceJIT::regT2);
-
-#if USE(JSVALUE32_64)
-    COMPILE_ASSERT(sizeof(void*) * 2 == sizeof(Register), Register_is_two_pointers_sized);
-    jit.lshift32(CCallHelpers::TrustedImm32(1), JSInterfaceJIT::regT2);
-#endif
-
-    // Do the sliding
-    MacroAssembler::Label copyLoop(jit.label());
-
-    jit.subPtr(CCallHelpers::TrustedImm32(sizeof(void*)), JSInterfaceJIT::callFrameRegister);
-    jit.sub32(CCallHelpers::TrustedImm32(1), JSInterfaceJIT::regT2);
-    jit.loadPtr(CCallHelpers::BaseIndex(JSInterfaceJIT::stackPointerRegister, JSInterfaceJIT::regT2, MacroAssembler::timesPtr()), JSInterfaceJIT::regT1);
-    jit.storePtr(JSInterfaceJIT::regT1, CCallHelpers::Address(JSInterfaceJIT::callFrameRegister));
-
-    jit.branchTest32(MacroAssembler::NonZero, JSInterfaceJIT::regT2).linkTo(copyLoop, &jit);
-
-    jit.emitFunctionEpilogue();
-}
-
 MacroAssemblerCodeRef baselineGetterReturnThunkGenerator(VM* vm)
 {
     JSInterfaceJIT jit(vm);

Modified: branches/jsc-tailcall/Source/_javascript_Core/jit/ThunkGenerators.h (187617 => 187618)


--- branches/jsc-tailcall/Source/_javascript_Core/jit/ThunkGenerators.h	2015-07-30 23:17:45 UTC (rev 187617)
+++ branches/jsc-tailcall/Source/_javascript_Core/jit/ThunkGenerators.h	2015-07-30 23:19:30 UTC (rev 187618)
@@ -49,8 +49,6 @@
 MacroAssemblerCodeRef arityFixupGenerator(VM*);
 MacroAssemblerCodeRef unreachableGenerator(VM*);
 
-void prepareForTailCall(CCallHelpers& jit);
-
 MacroAssemblerCodeRef baselineGetterReturnThunkGenerator(VM* vm);
 MacroAssemblerCodeRef baselineSetterReturnThunkGenerator(VM* vm);
 

Modified: branches/jsc-tailcall/Source/_javascript_Core/tests/stress/tail-call-no-stack-overflow.js (187617 => 187618)


--- branches/jsc-tailcall/Source/_javascript_Core/tests/stress/tail-call-no-stack-overflow.js	2015-07-30 23:17:45 UTC (rev 187617)
+++ branches/jsc-tailcall/Source/_javascript_Core/tests/stress/tail-call-no-stack-overflow.js	2015-07-30 23:19:30 UTC (rev 187618)
@@ -24,5 +24,14 @@
         return strictLoop(n - 1);
 }
 
+function strictLoopArityFixup(n, dummy) {
+    "use strict";
+    if (n > 0)
+        return strictLoopArityFixup(n - 1);
+}
+
 shouldThrow(function () { sloppyLoop(100000); }, 'RangeError: Maximum call stack size exceeded.');
+
+// These should not throw
 strictLoop(100000);
+strictLoopArityFixup(100000);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to