Changeset 190370 in webkit


Ignore:
Timestamp:
Sep 30, 2015 3:28:08 PM (9 years ago)
Author:
msaboff@apple.com
Message:

Source/JavaScriptCore:
Relanding r190289 with the following two fixes:

  1. REGRESSION(r190289): It made Speedometer/Full.html performance test fail https://bugs.webkit.org/show_bug.cgi?id=149621

Reviewed by Saam Barati.

We need to restore callee saves for both the fast and slow paths before making a
tail call in the FTL.

  • ftl/FTLJSCallBase.cpp: (JSC::FTL::JSCallBase::emit):
  1. [ARM] REGRESSION(r190289): It made 374 tests crash on 32 bit ARM Linux https://bugs.webkit.org/show_bug.cgi?id=149619

Reviewed by Filip Pizlo.

Need to check for ARMv7_TRADITIONAL and ARMv7 in addition to ARM in "if"
statement to handle platforms with a link register.


  • llint/LowLevelInterpreter.asm: (prepareForTailCall):

LayoutTests:
Relanding r190289 after fixes tracked in https://bugs.webkit.org/show_bug.cgi?id=149619
and https://bugs.webkit.org/show_bug.cgi?id=149621

Reviewed by Saam Barati.

Location:
trunk
Files:
8 added
34 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r190368 r190370  
     12015-09-30  Michael Saboff  <msaboff@apple.com>
     2
     3        Relanding r190289 after fixes tracked in https://bugs.webkit.org/show_bug.cgi?id=149619
     4        and https://bugs.webkit.org/show_bug.cgi?id=149621
     5
     6        Reviewed by Saam Barati.
     7
    182015-09-29  Simon Fraser  <simon.fraser@apple.com>
    29
  • trunk/LayoutTests/js/caller-property-expected.txt

    r190329 r190370  
    1111PASS strictCaller(nonStrictCallee) threw exception TypeError: Function.caller used to retrieve strict caller.
    1212PASS strictCaller(strictCallee) threw exception TypeError: Type error.
     13PASS strictTailCaller(nonStrictCallee) is null
     14PASS strictTailCaller(strictCallee) threw exception TypeError: Type error.
    1315PASS nonStrictCaller(boundNonStrictCallee) is nonStrictCaller
    1416PASS nonStrictCaller(boundStrictCallee) threw exception TypeError: Type error.
    1517PASS strictCaller(boundNonStrictCallee) threw exception TypeError: Function.caller used to retrieve strict caller.
    1618PASS strictCaller(boundStrictCallee) threw exception TypeError: Type error.
     19PASS strictTailCaller(boundNonStrictCallee) is null
     20PASS strictTailCaller(boundStrictCallee) threw exception TypeError: Type error.
    1721PASS nonStrictGetter(nonStrictAccessor) is nonStrictGetter
    1822PASS nonStrictSetter(nonStrictAccessor) is true
  • trunk/LayoutTests/js/script-tests/caller-property.js

    r190329 r190370  
    2424function strictCallee() { "use strict"; return strictCallee.caller; }
    2525function nonStrictCaller(x) { return x(); }
    26 function strictCaller(x) { "use strict"; return x(); }
     26// Tail calls leak and show our caller's caller, which is null here
     27function strictCaller(x) { "use strict"; var result = x(); return result; }
     28function strictTailCaller(x) { "use strict"; return x(); }
    2729shouldBe("nonStrictCaller(nonStrictCallee)", "nonStrictCaller");
    2830shouldThrow("nonStrictCaller(strictCallee)", '"TypeError: Type error"');
    2931shouldThrow("strictCaller(nonStrictCallee)", '"TypeError: Function.caller used to retrieve strict caller"');
    3032shouldThrow("strictCaller(strictCallee)", '"TypeError: Type error"');
     33shouldBe("strictTailCaller(nonStrictCallee)", "null");
     34shouldThrow("strictTailCaller(strictCallee)", '"TypeError: Type error"');
    3135
    3236// .caller within a bound function reaches the caller, ignoring the binding.
     
    3741shouldThrow("strictCaller(boundNonStrictCallee)", '"TypeError: Function.caller used to retrieve strict caller"');
    3842shouldThrow("strictCaller(boundStrictCallee)", '"TypeError: Type error"');
     43shouldBe("strictTailCaller(boundNonStrictCallee)", "null");
     44shouldThrow("strictTailCaller(boundStrictCallee)", '"TypeError: Type error"');
    3945
    4046// Check that .caller works (or throws) as expected, over an accessor call.
  • trunk/Source/JavaScriptCore/CMakeLists.txt

    r190367 r190370  
    905905        ftl/FTLJSCallBase.cpp
    906906        ftl/FTLJSCallVarargs.cpp
     907        ftl/FTLJSTailCall.cpp
    907908        ftl/FTLLink.cpp
    908909        ftl/FTLLocation.cpp
  • trunk/Source/JavaScriptCore/ChangeLog

    r190367 r190370  
     12015-09-30  Michael Saboff  <msaboff@apple.com>
     2
     3        Relanding r190289 with the following two fixes:
     4
     5         1. REGRESSION(r190289): It made Speedometer/Full.html performance test fail
     6            https://bugs.webkit.org/show_bug.cgi?id=149621
     7
     8            Reviewed by Saam Barati.
     9
     10            We need to restore callee saves for both the fast and slow paths before making a
     11            tail call in the FTL.
     12
     13            * ftl/FTLJSCallBase.cpp:
     14            (JSC::FTL::JSCallBase::emit):
     15
     16         2. [ARM] REGRESSION(r190289): It made 374 tests crash on 32 bit ARM Linux
     17            https://bugs.webkit.org/show_bug.cgi?id=149619
     18
     19            Reviewed by Filip Pizlo.
     20
     21            Need to check for ARMv7_TRADITIONAL and ARMv7 in addition to ARM in "if"
     22            statement to handle platforms with a link register.
     23           
     24            * llint/LowLevelInterpreter.asm:
     25            (prepareForTailCall):
     26
    1272015-09-30  Keith Miller  <keith_miller@apple.com>
    228
  • trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj

    r190367 r190370  
    540540    <ClCompile Include="..\ftl\FTLJSCallBase.cpp" />
    541541    <ClCompile Include="..\ftl\FTLJSCallVarargs.cpp" />
     542    <ClCompile Include="..\ftl\FTLJSTailCall.cpp" />
    542543    <ClCompile Include="..\ftl\FTLLink.cpp" />
    543544    <ClCompile Include="..\ftl\FTLLocation.cpp" />
     
    13011302    <ClInclude Include="..\ftl\FTLJSCallBase.h" />
    13021303    <ClInclude Include="..\ftl\FTLJSCallVarargs.h" />
     1304    <ClInclude Include="..\ftl\FTLJSTailCall.h" />
    13031305    <ClInclude Include="..\ftl\FTLLink.h" />
    13041306    <ClInclude Include="..\ftl\FTLLocation.h" />
  • trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters

    r190367 r190370  
    15851585      <Filter>ftl</Filter>
    15861586    </ClCompile>
     1587    <ClCompile Include="..\ftl\FTLJSTailCall.cpp">
     1588      <Filter>ftl</Filter>
     1589    </ClCompile>
    15871590    <ClCompile Include="..\ftl\FTLLink.cpp">
    15881591      <Filter>ftl</Filter>
     
    41524155    </ClInclude>
    41534156    <ClInclude Include="..\ftl\FTLJSCall.h">
     4157      <Filter>ftl</Filter>
     4158    </ClInclude>
     4159    <ClInclude Include="..\ftl\FTLJSTailCall.h">
    41544160      <Filter>ftl</Filter>
    41554161    </ClInclude>
  • trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj

    r190367 r190370  
    981981                627673231B680C1E00FD9F2E /* CallMode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 627673211B680C1E00FD9F2E /* CallMode.cpp */; };
    982982                627673241B680C1E00FD9F2E /* CallMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 627673221B680C1E00FD9F2E /* CallMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
     983                62774DAA1B8D4B190006F05A /* FTLJSTailCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62774DA81B8D4B190006F05A /* FTLJSTailCall.cpp */; };
     984                62774DAB1B8D4B190006F05A /* FTLJSTailCall.h in Headers */ = {isa = PBXBuildFile; fileRef = 62774DA91B8D4B190006F05A /* FTLJSTailCall.h */; };
    983985                62D2D38F1ADF103F000206C1 /* FunctionRareData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62D2D38D1ADF103F000206C1 /* FunctionRareData.cpp */; };
    984986                62D2D3901ADF103F000206C1 /* FunctionRareData.h in Headers */ = {isa = PBXBuildFile; fileRef = 62D2D38E1ADF103F000206C1 /* FunctionRareData.h */; settings = {ATTRIBUTES = (Private, ); }; };
     
    28052807                627673211B680C1E00FD9F2E /* CallMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CallMode.cpp; sourceTree = "<group>"; };
    28062808                627673221B680C1E00FD9F2E /* CallMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallMode.h; sourceTree = "<group>"; };
     2809                62774DA81B8D4B190006F05A /* FTLJSTailCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLJSTailCall.cpp; path = ftl/FTLJSTailCall.cpp; sourceTree = "<group>"; };
     2810                62774DA91B8D4B190006F05A /* FTLJSTailCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLJSTailCall.h; path = ftl/FTLJSTailCall.h; sourceTree = "<group>"; };
    28072811                62A9A29E1B0BED4800BD54CA /* DFGLazyNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGLazyNode.cpp; path = dfg/DFGLazyNode.cpp; sourceTree = "<group>"; };
    28082812                62A9A29F1B0BED4800BD54CA /* DFGLazyNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGLazyNode.h; path = dfg/DFGLazyNode.h; sourceTree = "<group>"; };
     
    39833987                                0FD120311A8C85BD000F5280 /* FTLJSCallVarargs.cpp */,
    39843988                                0FD120321A8C85BD000F5280 /* FTLJSCallVarargs.h */,
     3989                                62774DA81B8D4B190006F05A /* FTLJSTailCall.cpp */,
     3990                                62774DA91B8D4B190006F05A /* FTLJSTailCall.h */,
    39853991                                0F8F2B93172E049E007DBDA5 /* FTLLink.cpp */,
    39863992                                0F8F2B94172E049E007DBDA5 /* FTLLink.h */,
     
    63316337                                0FD120301A8AED12000F5280 /* FTLJSCallBase.h in Headers */,
    63326338                                0FD120341A8C85BD000F5280 /* FTLJSCallVarargs.h in Headers */,
     6339                                62774DAB1B8D4B190006F05A /* FTLJSTailCall.h in Headers */,
    63336340                                0F8F2B96172E04A3007DBDA5 /* FTLLink.h in Headers */,
    63346341                                0FCEFAE0180738C000472CE4 /* FTLLocation.h in Headers */,
     
    77037710                                0FD1202F1A8AED12000F5280 /* FTLJSCallBase.cpp in Sources */,
    77047711                                0FD120331A8C85BD000F5280 /* FTLJSCallVarargs.cpp in Sources */,
     7712                                62774DAA1B8D4B190006F05A /* FTLJSTailCall.cpp in Sources */,
    77057713                                0F8F2B95172E04A0007DBDA5 /* FTLLink.cpp in Sources */,
    77067714                                0FCEFADF180738C000472CE4 /* FTLLocation.cpp in Sources */,
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r190329 r190370  
    18831883        m_exitOK = true;
    18841884        processSetLocalQueue(); // This only comes into play for intrinsics, since normal inlined code will leave an empty queue.
    1885         addToGraph(Jump);
     1885        if (Node* terminal = m_currentBlock->terminal())
     1886            ASSERT_UNUSED(terminal, terminal->op() == TailCall || terminal->op() == TailCallVarargs);
     1887        else {
     1888            addToGraph(Jump);
     1889            landingBlocks.append(m_currentBlock);
     1890        }
    18861891        if (verbose)
    18871892            dataLog("Marking ", RawPointer(m_currentBlock), " as linked (tail of poly inlinee)\n");
    18881893        m_currentBlock->didLink();
    1889         landingBlocks.append(m_currentBlock);
    18901894
    18911895        if (verbose)
     
    19201924    m_exitOK = true; // Origin changed, so it's fine to exit again.
    19211925    processSetLocalQueue();
    1922     addToGraph(Jump);
    1923     landingBlocks.append(m_currentBlock);
     1926    if (Node* terminal = m_currentBlock->terminal())
     1927        ASSERT_UNUSED(terminal, terminal->op() == TailCall || terminal->op() == TailCallVarargs);
     1928    else {
     1929        addToGraph(Jump);
     1930        landingBlocks.append(m_currentBlock);
     1931    }
    19241932   
    19251933    RefPtr<BasicBlock> continuationBlock = adoptRef(
     
    36653673                Node* terminal = m_currentBlock->terminal();
    36663674                ASSERT_UNUSED(terminal, terminal->op() == TailCall || terminal->op() == TailCallVarargs);
    3667                 LAST_OPCODE(op_ret);
     3675                LAST_OPCODE(op_jmp);
    36683676            }
    36693677            int relativeOffset = currentInstruction[1].u.operand;
  • trunk/Source/JavaScriptCore/dfg/DFGClobberize.h

    r190329 r190370  
    10361036    case ThrowReferenceError:
    10371037        write(SideState);
    1038         read(HeapObjectCount);
    1039         write(HeapObjectCount);
    10401038        return;
    10411039       
  • trunk/Source/JavaScriptCore/dfg/DFGNode.h

    r190329 r190370  
    11171117            return false;
    11181118        }
     1119    }
     1120
     1121    bool isFunctionTerminal()
     1122    {
     1123        if (isTerminal() && !numSuccessors())
     1124            return true;
     1125
     1126        return false;
    11191127    }
    11201128
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

    r190329 r190370  
    772772            callee.use();
    773773
     774            shuffleData.tagTypeNumber = GPRInfo::tagTypeNumberRegister;
    774775            shuffleData.numLocals = m_jit.graph().frameRegisterCount();
    775776            shuffleData.callee = ValueRecovery::inGPR(calleeGPR, DataFormatJS);
     
    869870    }
    870871
    871     callLinkInfo->setUpCall(callType, m_currentNode->origin.semantic,  calleeGPR);   
     872    callLinkInfo->setUpCall(callType, m_currentNode->origin.semantic, calleeGPR);
    872873    m_jit.addJSCall(fastCall, slowCall, targetToCheck, callLinkInfo);
    873874}
  • trunk/Source/JavaScriptCore/dfg/DFGTierUpCheckInjectionPhase.cpp

    r190329 r190370  
    9696           
    9797            NodeAndIndex terminal = block->findTerminal();
    98             if (terminal.node->op() == Return) {
     98            if (terminal.node->isFunctionTerminal()) {
    9999                insertionSet.insertNode(
    100100                    terminal.index, SpecNone, CheckTierUpAtReturn, terminal.node->origin);
  • trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp

    r190329 r190370  
    131131    case StoreBarrier:
    132132    case Call:
     133    case TailCall:
     134    case TailCallInlinedCaller:
    133135    case Construct:
    134136    case CallVarargs:
     137    case TailCallVarargs:
     138    case TailCallVarargsInlinedCaller:
     139    case ConstructVarargs:
    135140    case CallForwardVarargs:
    136     case ConstructVarargs:
     141    case TailCallForwardVarargs:
     142    case TailCallForwardVarargsInlinedCaller:
    137143    case ConstructForwardVarargs:
    138144    case LoadVarargs:
  • trunk/Source/JavaScriptCore/ftl/FTLCompile.cpp

    r190329 r190370  
    619619        });
    620620    }
     621
     622    adjustCallICsForStackmaps(state.jsTailCalls, recordMap);
     623
     624    for (unsigned i = state.jsTailCalls.size(); i--;) {
     625        JSTailCall& call = state.jsTailCalls[i];
     626
     627        CCallHelpers fastPathJIT(&vm, codeBlock);
     628        call.emit(*state.jitCode.get(), fastPathJIT);
     629
     630        char* startOfIC = bitwise_cast<char*>(generatedFunction) + call.m_instructionOffset;
     631        size_t sizeOfIC = call.estimatedSize();
     632
     633        generateInlineIfPossibleOutOfLineIfNot(state, vm, codeBlock, fastPathJIT, startOfIC, sizeOfIC, "tail call inline cache", [&] (LinkBuffer& linkBuffer, CCallHelpers&, bool) {
     634            call.link(vm, linkBuffer);
     635        });
     636    }
    621637   
    622638    auto iter = recordMap.find(state.handleStackOverflowExceptionStackmapID);
  • trunk/Source/JavaScriptCore/ftl/FTLInlineCacheSize.cpp

    r190329 r190370  
    8383}
    8484
     85size_t sizeOfTailCallVarargs()
     86{
     87#if CPU(ARM64)
     88    return 188 + sizeOfCallVarargs();
     89#else
     90    return 151 + sizeOfCallVarargs();
     91#endif
     92}
     93
    8594size_t sizeOfCallForwardVarargs()
    8695{
     
    8998#else
    9099    return 262;
     100#endif
     101}
     102
     103size_t sizeOfTailCallForwardVarargs()
     104{
     105#if CPU(ARM64)
     106    return 188 + sizeOfCallForwardVarargs();
     107#else
     108    return 151 + sizeOfCallForwardVarargs();
    91109#endif
    92110}
     
    122140        return sizeOfCall();
    123141    case CallVarargs:
     142    case TailCallVarargsInlinedCaller:
    124143        return sizeOfCallVarargs();
     144    case TailCallVarargs:
     145        return sizeOfTailCallVarargs();
    125146    case CallForwardVarargs:
     147    case TailCallForwardVarargsInlinedCaller:
    126148        return sizeOfCallForwardVarargs();
     149    case TailCallForwardVarargs:
     150        return sizeOfTailCallForwardVarargs();
    127151    case ConstructVarargs:
    128152        return sizeOfConstructVarargs();
     
    132156        return sizeOfIn();
    133157    default:
    134         return 0;
     158        RELEASE_ASSERT_NOT_REACHED();
    135159    }
    136160}
  • trunk/Source/JavaScriptCore/ftl/FTLInlineCacheSize.h

    r190329 r190370  
    4141size_t sizeOfCall();
    4242size_t sizeOfCallVarargs();
     43size_t sizeOfTailCallVarargs();
    4344size_t sizeOfCallForwardVarargs();
     45size_t sizeOfTailCallForwardVarargs();
    4446size_t sizeOfConstructVarargs();
    4547size_t sizeOfConstructForwardVarargs();
  • trunk/Source/JavaScriptCore/ftl/FTLJSCall.cpp

    r190329 r190370  
    4949    , m_instructionOffset(0)
    5050{
    51     ASSERT(node->op() == Call || node->op() == Construct);
     51    ASSERT(node->op() == Call || node->op() == Construct || node->op() == TailCallInlinedCaller);
    5252}
    5353
  • trunk/Source/JavaScriptCore/ftl/FTLJSCallBase.cpp

    r190329 r190370  
    5353    m_callLinkInfo = jit.codeBlock()->addCallLinkInfo();
    5454   
     55    if (CallLinkInfo::callModeFor(m_type) == CallMode::Tail)
     56        jit.emitRestoreCalleeSaves();
     57
    5558    CCallHelpers::Jump slowPath = jit.branchPtrWithPatch(
    5659        CCallHelpers::NotEqual, GPRInfo::regT0, m_targetToCheck,
    5760        CCallHelpers::TrustedImmPtr(0));
    58    
    59     m_fastCall = jit.nearCall();
    60     CCallHelpers::Jump done = jit.jump();
    61    
     61
     62    CCallHelpers::Jump done;
     63
     64    if (CallLinkInfo::callModeFor(m_type) == CallMode::Tail) {
     65        jit.prepareForTailCallSlow();
     66        m_fastCall = jit.nearTailCall();
     67    } else {
     68        m_fastCall = jit.nearCall();
     69        done = jit.jump();
     70    }
     71
    6272    slowPath.link(&jit);
    63    
     73
    6474    jit.move(CCallHelpers::TrustedImmPtr(m_callLinkInfo), GPRInfo::regT2);
    6575    m_slowCall = jit.nearCall();
    66    
    67     done.link(&jit);
     76
     77    if (CallLinkInfo::callModeFor(m_type) == CallMode::Tail)
     78        jit.abortWithReason(JITDidReturnFromTailCall);
     79    else
     80        done.link(&jit);
     81
     82    m_callLinkInfo->setUpCall(m_type, m_origin, GPRInfo::regT0);
    6883}
    6984
     
    7388        m_slowCall, FunctionPtr(vm.getCTIStub(linkCallThunkGenerator).code().executableAddress()));
    7489
    75     m_callLinkInfo->setUpCallFromFTL(m_type, m_origin, linkBuffer.locationOfNearCall(m_slowCall),
    76         linkBuffer.locationOf(m_targetToCheck), linkBuffer.locationOfNearCall(m_fastCall),
    77         GPRInfo::regT0);
     90    m_callLinkInfo->setCallLocations(linkBuffer.locationOfNearCall(m_slowCall),
     91        linkBuffer.locationOf(m_targetToCheck), linkBuffer.locationOfNearCall(m_fastCall));
    7892}
    7993
  • trunk/Source/JavaScriptCore/ftl/FTLJSCallBase.h

    r190329 r190370  
    5151    void link(VM&, LinkBuffer&);
    5252   
    53 private:
     53protected:
    5454    CallLinkInfo::CallType m_type;
    5555    CodeOrigin m_origin;
  • trunk/Source/JavaScriptCore/ftl/FTLJSCallVarargs.cpp

    r190329 r190370  
    5252    , m_callBase(
    5353        (node->op() == ConstructVarargs || node->op() == ConstructForwardVarargs)
    54         ? CallLinkInfo::ConstructVarargs : CallLinkInfo::CallVarargs,
     54        ? CallLinkInfo::ConstructVarargs : (node->op() == TailCallVarargs || node->op() == TailCallForwardVarargs)
     55        ? CallLinkInfo::TailCallVarargs : CallLinkInfo::CallVarargs,
    5556        node->origin.semantic)
    5657    , m_instructionOffset(0)
     
    5859    ASSERT(
    5960        node->op() == CallVarargs || node->op() == CallForwardVarargs
     61        || node->op() == TailCallVarargsInlinedCaller || node->op() == TailCallForwardVarargsInlinedCaller
     62        || node->op() == TailCallVarargs || node->op() == TailCallForwardVarargs
    6063        || node->op() == ConstructVarargs || node->op() == ConstructForwardVarargs);
    6164}
     
    8487    switch (m_node->op()) {
    8588    case CallVarargs:
     89    case TailCallVarargs:
     90    case TailCallVarargsInlinedCaller:
    8691    case ConstructVarargs:
    8792        argumentsGPR = GPRInfo::argumentGPR1;
     
    8994        break;
    9095    case CallForwardVarargs:
     96    case TailCallForwardVarargs:
     97    case TailCallForwardVarargsInlinedCaller:
    9198    case ConstructForwardVarargs:
    9299        thisGPR = GPRInfo::argumentGPR1;
     
    197204    // stack frame to already be set up, which it is.
    198205    jit.store64(GPRInfo::regT0, CCallHelpers::calleeFrameSlot(JSStack::Callee));
    199    
     206
    200207    m_callBase.emit(jit);
    201208   
  • trunk/Source/JavaScriptCore/ftl/FTLLocation.h

    r190329 r190370  
    121121    }
    122122   
    123     bool operator!() const { return kind() == Unprocessed && !u.variable.offset; }
     123    explicit operator bool() const { return kind() != Unprocessed || u.variable.offset; }
     124
     125    bool operator!() const { return !static_cast<bool>(*this); }
    124126   
    125127    bool isHashTableDeletedValue() const { return kind() == Unprocessed && u.variable.offset; }
  • trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp

    r190329 r190370  
    174174                switch (node->op()) {
    175175                case CallVarargs:
     176                case TailCallVarargs:
     177                case TailCallVarargsInlinedCaller:
    176178                case CallForwardVarargs:
     179                case TailCallForwardVarargs:
     180                case TailCallForwardVarargsInlinedCaller:
    177181                case ConstructVarargs:
    178182                case ConstructForwardVarargs:
     
    724728            break;
    725729        case Call:
     730        case TailCallInlinedCaller:
    726731        case Construct:
    727732            compileCallOrConstruct();
    728733            break;
     734        case TailCall:
     735            compileTailCall();
     736            break;
    729737        case CallVarargs:
    730738        case CallForwardVarargs:
     739        case TailCallVarargs:
     740        case TailCallVarargsInlinedCaller:
     741        case TailCallForwardVarargs:
     742        case TailCallForwardVarargsInlinedCaller:
    731743        case ConstructVarargs:
    732744        case ConstructForwardVarargs:
     
    44014413        setJSValue(call);
    44024414    }
     4415
     4416    void compileTailCall()
     4417    {
     4418        int numArgs = m_node->numChildren() - 1;
     4419        ExitArgumentList exitArguments;
     4420        exitArguments.reserveCapacity(numArgs + 6);
     4421
     4422        unsigned stackmapID = m_stackmapIDs++;
     4423        exitArguments.append(lowJSValue(m_graph.varArgChild(m_node, 0)));
     4424        exitArguments.append(m_tagTypeNumber);
     4425
     4426        Vector<ExitValue> callArguments(numArgs);
     4427
     4428        bool needsTagTypeNumber { false };
     4429        for (int i = 0; i < numArgs; ++i) {
     4430            callArguments[i] =
     4431                exitValueForTailCall(exitArguments, m_graph.varArgChild(m_node, 1 + i).node());
     4432            if (callArguments[i].dataFormat() == DataFormatInt32)
     4433                needsTagTypeNumber = true;
     4434        }
     4435
     4436        JSTailCall tailCall(stackmapID, m_node, WTF::move(callArguments));
     4437
     4438        exitArguments.insert(0, m_out.constInt32(needsTagTypeNumber ? 2 : 1));
     4439        exitArguments.insert(0, constNull(m_out.ref8));
     4440        exitArguments.insert(0, m_out.constInt32(tailCall.estimatedSize()));
     4441        exitArguments.insert(0, m_out.constInt64(stackmapID));
     4442
     4443        LValue call =
     4444            m_out.call(m_out.patchpointVoidIntrinsic(), exitArguments);
     4445        setInstructionCallingConvention(call, LLVMAnyRegCallConv);
     4446        m_out.unreachable();
     4447
     4448        m_ftlState.jsTailCalls.append(tailCall);
     4449    }
    44034450   
    44044451    void compileCallOrConstructVarargs()
     
    44114458        switch (m_node->op()) {
    44124459        case CallVarargs:
     4460        case TailCallVarargs:
     4461        case TailCallVarargsInlinedCaller:
    44134462        case ConstructVarargs:
    44144463            jsArguments = lowJSValue(m_node->child2());
    44154464            break;
    44164465        case CallForwardVarargs:
     4466        case TailCallForwardVarargs:
     4467        case TailCallForwardVarargsInlinedCaller:
    44174468        case ConstructForwardVarargs:
    44184469            break;
     
    44414492       
    44424493        m_ftlState.jsCallVarargses.append(JSCallVarargs(stackmapID, m_node));
    4443        
    4444         setJSValue(call);
     4494
     4495        switch (m_node->op()) {
     4496        case TailCallVarargs:
     4497        case TailCallForwardVarargs:
     4498            m_out.unreachable();
     4499            break;
     4500
     4501        default:
     4502            setJSValue(call);
     4503        }
    44454504    }
    44464505   
     
    82578316    void callPreflight()
    82588317    {
    8259         callPreflight(m_node->origin.semantic);
     8318        CodeOrigin codeOrigin = m_node->origin.semantic;
     8319
     8320        if (m_node->op() == TailCallInlinedCaller
     8321            || m_node->op() == TailCallVarargsInlinedCaller
     8322            || m_node->op() == TailCallForwardVarargsInlinedCaller)
     8323            codeOrigin =*codeOrigin.inlineCallFrame->getCallerSkippingDeadFrames();
     8324
     8325        callPreflight(codeOrigin);
    82608326    }
    82618327   
     
    85288594        return ExitValue::dead();
    85298595    }
    8530    
     8596
    85318597    ExitValue exitArgument(ExitArgumentList& arguments, DataFormat format, LValue value)
    85328598    {
     
    85348600        arguments.append(value);
    85358601        return result;
     8602    }
     8603
     8604    ExitValue exitValueForTailCall(ExitArgumentList& arguments, Node* node)
     8605    {
     8606        ASSERT(node->shouldGenerate());
     8607        ASSERT(node->hasResult());
     8608
     8609        switch (node->op()) {
     8610        case JSConstant:
     8611        case Int52Constant:
     8612        case DoubleConstant:
     8613            return ExitValue::constant(node->asJSValue());
     8614
     8615        default:
     8616            break;
     8617        }
     8618
     8619        LoweredNodeValue value = m_jsValueValues.get(node);
     8620        if (isValid(value))
     8621            return exitArgument(arguments, DataFormatJS, value.value());
     8622
     8623        value = m_int32Values.get(node);
     8624        if (isValid(value))
     8625            return exitArgument(arguments, DataFormatInt32, value.value());
     8626
     8627        value = m_booleanValues.get(node);
     8628        if (isValid(value)) {
     8629            LValue valueToPass = m_out.zeroExt(value.value(), m_out.int32);
     8630            return exitArgument(arguments, DataFormatBoolean, valueToPass);
     8631        }
     8632
     8633        // Doubles and Int52 have been converted by ValueRep()
     8634        DFG_CRASH(m_graph, m_node, toCString("Cannot find value for node: ", node).data());
    85368635    }
    85378636   
  • trunk/Source/JavaScriptCore/ftl/FTLState.h

    r190329 r190370  
    3838#include "FTLJSCall.h"
    3939#include "FTLJSCallVarargs.h"
     40#include "FTLJSTailCall.h"
    4041#include "FTLStackMaps.h"
    4142#include "FTLState.h"
     
    8081    Vector<JSCall> jsCalls;
    8182    Vector<JSCallVarargs> jsCallVarargses;
     83    Vector<JSTailCall> jsTailCalls;
    8284    Vector<CString> codeSectionNames;
    8385    Vector<CString> dataSectionNames;
  • trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp

    r190329 r190370  
    339339    if (width == NormalJumpWidth)
    340340        return result;
    341    
     341
    342342    PatchableJump realJump = patchableJump();
    343343    result.link(this);
  • trunk/Source/JavaScriptCore/jit/CallFrameShuffleData.h

    r190329 r190370  
    4040#if USE(JSVALUE64)
    4141    RegisterMap<ValueRecovery> registers;
     42    GPRReg tagTypeNumber { InvalidGPRReg };
    4243
    4344    void setupCalleeSaveRegisters(CodeBlock*);
  • trunk/Source/JavaScriptCore/jit/CallFrameShuffler.cpp

    r190329 r190370  
    7272            addNew(reg.fpr(), data.registers[reg]);
    7373    }
     74
     75    m_tagTypeNumber = data.tagTypeNumber;
     76    if (m_tagTypeNumber != InvalidGPRReg)
     77        lockGPR(m_tagTypeNumber);
    7478#endif
    7579}
     
    8185    static const char* dangerBoundsDelimiter = " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ";
    8286    static const char* emptySpace            = "                                   ";
    83     ASSERT(m_alignedNewFrameSize <= numLocals());
    8487    out.print("          ");
    8588    out.print("           Old frame               ");
    8689    out.print("           New frame               ");
    8790    out.print("\n");
    88     for (int i = 0; i < m_alignedOldFrameSize + numLocals() + 3; ++i) {
     91    int totalSize = m_alignedOldFrameSize + std::max(numLocals(), m_alignedNewFrameSize) + 3;
     92    for (int i = 0; i < totalSize; ++i) {
    8993        VirtualRegister old { m_alignedOldFrameSize - i - 1 };
    9094        VirtualRegister newReg { old + m_frameDelta };
     
    205209    if (m_newFrameOffset)
    206210        out.print("   New frame offset is ", m_newFrameOffset, "\n");
     211#if USE(JSVALUE64)
     212    if (m_tagTypeNumber != InvalidGPRReg)
     213        out.print("   TagTypeNumber is currently in ", m_tagTypeNumber, "\n");
     214#endif
    207215}
    208216
     
    248256
    249257    VirtualRegister spillSlot { 0 };
    250     for (VirtualRegister slot = firstOld(); slot <= lastOld(); slot -= 1) {
    251         ASSERT(slot < newAsOld(firstNew()));
     258    for (VirtualRegister slot = firstOld(); slot <= lastOld(); slot += 1) {
     259        if (slot >= newAsOld(firstNew()))
     260            break;
     261
    252262        if (getOld(slot))
    253263            continue;
     
    256266        break;
    257267    }
    258     // We must have enough slots to be able to fit the whole
    259     // callee's frame for the slow path.
    260     RELEASE_ASSERT(spillSlot.isLocal());
     268    // We must have enough slots to be able to fit the whole callee's
     269    // frame for the slow path - unless we are in the FTL. In that
     270    // case, we are allowed to extend the frame *once*, since we are
     271    // guaranteed to have enough available space for that.
     272    if (spillSlot >= newAsOld(firstNew()) || !spillSlot.isLocal()) {
     273        RELEASE_ASSERT(!m_didExtendFrame);
     274        extendFrameIfNeeded();
     275        spill(cachedRecovery);
     276        return;
     277    }
    261278
    262279    if (verbose)
     
    287304}
    288305
     306void CallFrameShuffler::extendFrameIfNeeded()
     307{
     308    ASSERT(!m_didExtendFrame);
     309    ASSERT(!isUndecided());
     310
     311    VirtualRegister firstRead { firstOld() };
     312    for (; firstRead <= virtualRegisterForLocal(0); firstRead += 1) {
     313        if (getOld(firstRead))
     314            break;
     315    }
     316    size_t availableSize = static_cast<size_t>(firstRead.offset() - firstOld().offset());
     317    size_t wantedSize = m_newFrame.size() + m_newFrameOffset;
     318
     319    if (availableSize < wantedSize) {
     320        size_t delta = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), wantedSize - availableSize);
     321        m_oldFrame.grow(m_oldFrame.size() + delta);
     322        for (size_t i = 0; i < delta; ++i)
     323            m_oldFrame[m_oldFrame.size() - i - 1] = nullptr;
     324        m_jit.subPtr(MacroAssembler::TrustedImm32(delta * sizeof(Register)), MacroAssembler::stackPointerRegister);
     325
     326        if (isSlowPath())
     327            m_frameDelta = numLocals() + JSStack::CallerFrameAndPCSize;
     328        else
     329            m_oldFrameOffset = numLocals();
     330
     331        if (verbose)
     332            dataLogF("  Not enough space - extending the old frame %zu slot\n", delta);
     333    }
     334
     335    m_didExtendFrame = true;
     336}
     337
    289338void CallFrameShuffler::prepareForSlowPath()
    290339{
     
    297346
    298347    if (verbose)
    299         dataLog("\n\nPreparing frame for slow path call:\n", *this);
     348        dataLog("\n\nPreparing frame for slow path call:\n");
     349
     350    // When coming from the FTL, we need to extend the frame. In other
     351    // cases, we may end up extending the frame if we previously
     352    // spilled things (e.g. in polymorphic cache).
     353    extendFrameIfNeeded();
     354
     355    if (verbose)
     356        dataLog(*this);
    300357
    301358    prepareAny();
     
    647704    }
    648705
     706#if USE(JSVALUE64)
     707    if (m_tagTypeNumber != InvalidGPRReg && m_newRegisters[m_tagTypeNumber])
     708        releaseGPR(m_tagTypeNumber);
     709#endif
     710
    649711    // Handle 2) by loading all registers. We don't have to do any
    650712    // writes, since they have been taken care of above.
     
    660722        ASSERT(cachedRecovery->targets().isEmpty());
    661723    }
     724
     725#if USE(JSVALUE64)
     726    if (m_tagTypeNumber != InvalidGPRReg)
     727        releaseGPR(m_tagTypeNumber);
     728#endif
    662729
    663730    // At this point, we have read everything we cared about from the
  • trunk/Source/JavaScriptCore/jit/CallFrameShuffler.h

    r190329 r190370  
    7272        }
    7373        m_lockedRegisters.clear(gpr);
     74    }
     75
     76    void restoreGPR(GPRReg gpr)
     77    {
     78        if (!m_newRegisters[gpr])
     79            return;
     80
     81        ensureGPR();
     82#if USE(JSVALUE32_64)
     83        GPRReg tempGPR { getFreeGPR() };
     84        lockGPR(tempGPR);
     85        ensureGPR();
     86        releaseGPR(tempGPR);
     87#endif
     88        emitDisplace(*m_newRegisters[gpr]);
    7489    }
    7590
     
    310325    }
    311326
     327    bool m_didExtendFrame { false };
     328
     329    void extendFrameIfNeeded();
     330
    312331    // This stores, for each slot in the new frame, information about
    313332    // the recovery for the value that should eventually go into that
     
    386405    // ensure that we have at least 2 available registers for loading
    387406    // a pair on 32bits.
    388     RegisterSet m_lockedRegisters;
     407    mutable RegisterSet m_lockedRegisters;
    389408
    390409    // This stores the current recoveries present in registers. A null
     
    392411    // care about it.
    393412    RegisterMap<CachedRecovery*> m_registers;
     413
     414#if USE(JSVALUE64)
     415    mutable GPRReg m_tagTypeNumber;
     416
     417    bool tryAcquireTagTypeNumber();
     418#endif
    394419
    395420    // This stores, for each register, information about the recovery
     
    422447            }
    423448        }
     449
     450#if USE(JSVALUE64)
     451        if (!nonTemp && m_tagTypeNumber != InvalidGPRReg && check(Reg { m_tagTypeNumber })) {
     452            ASSERT(m_lockedRegisters.get(m_tagTypeNumber));
     453            m_lockedRegisters.clear(m_tagTypeNumber);
     454            nonTemp = Reg { m_tagTypeNumber };
     455            m_tagTypeNumber = InvalidGPRReg;
     456        }
     457#endif
    424458        return nonTemp;
     459    }
     460
     461    GPRReg getFreeTempGPR() const
     462    {
     463        Reg freeTempGPR { getFreeRegister([this] (Reg reg) { return reg.isGPR() && !m_newRegisters[reg]; }) };
     464        if (!freeTempGPR)
     465            return InvalidGPRReg;
     466        return freeTempGPR.gpr();
    425467    }
    426468
     
    520562    }
    521563
     564    void ensureTempGPR()
     565    {
     566        if (getFreeTempGPR() != InvalidGPRReg)
     567            return;
     568
     569        if (verbose)
     570            dataLog("  Finding a temp GPR to spill\n");
     571        ensureRegister(
     572            [this] (const CachedRecovery& cachedRecovery) {
     573                if (cachedRecovery.recovery().isInGPR()) {
     574                    return !m_lockedRegisters.get(cachedRecovery.recovery().gpr())
     575                        && !m_newRegisters[cachedRecovery.recovery().gpr()];
     576                }
     577#if USE(JSVALUE32_64)
     578                if (cachedRecovery.recovery().technique() == InPair) {
     579                    return !m_lockedRegisters.get(cachedRecovery.recovery().tagGPR())
     580                        && !m_lockedRegisters.get(cachedRecovery.recovery().payloadGPR())
     581                        && !m_newRegisters[cachedRecovery.recovery().tagGPR()]
     582                        && !m_newRegisters[cachedRecovery.recovery().payloadGPR()];
     583                }
     584#endif
     585                return false;
     586            });
     587    }
     588
    522589    void ensureGPR()
    523590    {
     
    574641        ASSERT(jsValueRegs && !getNew(jsValueRegs));
    575642        CachedRecovery* cachedRecovery = addCachedRecovery(recovery);
    576         ASSERT(!cachedRecovery->wantedJSValueRegs());
    577         cachedRecovery->setWantedJSValueRegs(jsValueRegs);
    578643#if USE(JSVALUE64)
     644        if (cachedRecovery->wantedJSValueRegs())
     645            m_newRegisters[cachedRecovery->wantedJSValueRegs().gpr()] = nullptr;
    579646        m_newRegisters[jsValueRegs.gpr()] = cachedRecovery;
    580647#else
     648        if (JSValueRegs oldRegs { cachedRecovery->wantedJSValueRegs() }) {
     649            if (oldRegs.payloadGPR())
     650                m_newRegisters[oldRegs.payloadGPR()] = nullptr;
     651            if (oldRegs.tagGPR())
     652                m_newRegisters[oldRegs.tagGPR()] = nullptr;
     653        }
    581654        if (jsValueRegs.payloadGPR() != InvalidGPRReg)
    582655            m_newRegisters[jsValueRegs.payloadGPR()] = cachedRecovery;
     
    584657            m_newRegisters[jsValueRegs.tagGPR()] = cachedRecovery;
    585658#endif
     659        ASSERT(!cachedRecovery->wantedJSValueRegs());
     660        cachedRecovery->setWantedJSValueRegs(jsValueRegs);
    586661    }
    587662
  • trunk/Source/JavaScriptCore/jit/CallFrameShuffler64.cpp

    r190329 r190370  
    8888                cachedRecovery.recovery().gpr(),
    8989                cachedRecovery.recovery().gpr());
    90             // We have to do this the hard way.
    91             m_jit.or64(MacroAssembler::TrustedImm64(TagTypeNumber),
    92                 cachedRecovery.recovery().gpr());
     90            m_lockedRegisters.set(cachedRecovery.recovery().gpr());
     91            if (tryAcquireTagTypeNumber())
     92                m_jit.or64(m_tagTypeNumber, cachedRecovery.recovery().gpr());
     93            else {
     94                // We have to do this the hard way
     95                m_jit.or64(MacroAssembler::TrustedImm64(TagTypeNumber),
     96                    cachedRecovery.recovery().gpr());
     97            }
     98            m_lockedRegisters.clear(cachedRecovery.recovery().gpr());
    9399            cachedRecovery.setRecovery(
    94100                ValueRecovery::inGPR(cachedRecovery.recovery().gpr(), DataFormatJS));
     
    142148            m_jit.purifyNaN(cachedRecovery.recovery().fpr());
    143149            m_jit.moveDoubleTo64(cachedRecovery.recovery().fpr(), resultGPR);
    144             m_jit.sub64(MacroAssembler::TrustedImm64(TagTypeNumber), resultGPR);
     150            m_lockedRegisters.set(resultGPR);
     151            if (tryAcquireTagTypeNumber())
     152                m_jit.sub64(m_tagTypeNumber, resultGPR);
     153            else
     154                m_jit.sub64(MacroAssembler::TrustedImm64(TagTypeNumber), resultGPR);
     155            m_lockedRegisters.clear(resultGPR);
    145156            updateRecovery(cachedRecovery, ValueRecovery::inGPR(resultGPR, DataFormatJS));
    146157            if (verbose)
     
    338349    ASSERT(m_registers[wantedReg] == &cachedRecovery);
    339350}
     351   
     352bool CallFrameShuffler::tryAcquireTagTypeNumber()
     353{
     354    if (m_tagTypeNumber != InvalidGPRReg)
     355        return true;
     356
     357    m_tagTypeNumber = getFreeGPR();
     358
     359    if (m_tagTypeNumber == InvalidGPRReg)
     360        return false;
     361
     362    m_lockedRegisters.set(m_tagTypeNumber);
     363    m_jit.move(MacroAssembler::TrustedImm64(TagTypeNumber), m_tagTypeNumber);
     364    return true;
     365}
    340366
    341367} // namespace JSC
  • trunk/Source/JavaScriptCore/jit/JITCall.cpp

    r190329 r190370  
    194194    if (opcodeID == op_tail_call) {
    195195        CallFrameShuffleData shuffleData;
     196        shuffleData.tagTypeNumber = GPRInfo::tagTypeNumberRegister;
    196197        shuffleData.numLocals =
    197198            instruction[4].u.operand - sizeof(CallerFrameAndPC) / sizeof(Register);
  • trunk/Source/JavaScriptCore/jit/Reg.h

    r190329 r190370  
    5656    {
    5757    }
     58
     59    Reg(WTF::HashTableDeletedValueType)
     60        : m_index(deleted())
     61    {
     62    }
    5863   
    5964    Reg(MacroAssembler::RegisterID reg)
     
    103108    bool operator!() const { return !isSet(); }
    104109    explicit operator bool() const { return isSet(); }
     110
     111    bool isHashTableDeletedValue() const { return m_index == deleted(); }
    105112   
    106113    bool isGPR() const
     
    166173private:
    167174    static uint8_t invalid() { return 0xff; }
     175
     176    static uint8_t deleted() { return 0xfe; }
    168177   
    169178    uint8_t m_index;
    170179};
    171180
     181struct RegHash {
     182    static unsigned hash(const Reg& key) { return key.hash(); }
     183    static bool equal(const Reg& a, const Reg& b) { return a == b; }
     184    static const bool safeToCompareToEmptyOrDeleted = true;
     185};
     186
    172187} // namespace JSC
    173188
     189namespace WTF {
     190
     191template<typename T> struct DefaultHash;
     192template<> struct DefaultHash<JSC::Reg> {
     193    typedef JSC::RegHash Hash;
     194};
     195
     196template<typename T> struct HashTraits;
     197template<> struct HashTraits<JSC::Reg> : SimpleClassHashTraits<JSC::Reg> {
     198    static const bool emptyValueIsZero = false;
     199 };
     200
     201} // namespace WTF
     202
    174203#endif // ENABLE(JIT)
    175204
  • trunk/Source/JavaScriptCore/llint/LowLevelInterpreter.asm

    r190076 r190370  
    754754    andi ~StackAlignmentMask, temp2
    755755
    756     if ARM or SH4 or ARM64 or C_LOOP or MIPS
     756    if ARM or ARMv7_TRADITIONAL or ARMv7 or SH4 or ARM64 or C_LOOP or MIPS
    757757        addp 2 * PtrSize, sp
    758758        subi 2 * PtrSize, temp2
  • trunk/Source/JavaScriptCore/runtime/Options.h

    r190329 r190370  
    129129    \
    130130    v(bool, enableFunctionDotArguments, true, nullptr) \
    131     v(bool, enableTailCalls, false, nullptr) \
     131    v(bool, enableTailCalls, true, nullptr) \
    132132    \
    133133    /* showDisassembly implies showDFGDisassembly. */ \
  • trunk/Source/JavaScriptCore/tests/es6.yaml

    r190367 r190370  
    878878  cmd: runES6 :fail
    879879- path: es6/proper_tail_calls_tail_call_optimisation_direct_recursion.js
    880   cmd: runES6 :fail
     880  cmd: runES6 :normal
    881881- path: es6/proper_tail_calls_tail_call_optimisation_mutual_recursion.js
    882   cmd: runES6 :fail
     882  cmd: runES6 :normal
    883883- path: es6/prototype_of_bound_functions_arrow_functions.js
    884884  cmd: runES6 :fail
Note: See TracChangeset for help on using the changeset viewer.