Changeset 138921 in webkit


Ignore:
Timestamp:
Jan 6, 2013 6:24:58 PM (11 years ago)
Author:
fpizlo@apple.com
Message:

DFG should inline closure calls
https://bugs.webkit.org/show_bug.cgi?id=106067

Reviewed by Gavin Barraclough.

This adds initial support for inlining closure calls to the DFG. A call is considered
to be a closure call when the JSFunction* varies, but always has the same executable.
We already have closure call inline caching in both JITs, which works by checking that
the callee has an expected structure (as a cheap way of detecting that it is in fact
a JSFunction) and an expected executable. Closure call inlining uses profiling data
aggregated by CallLinkStatus to decide when to specialize the call to the particular
structure/executable, and inline the call rather than emitting a call sequence. When
we choose to do a closure inline rather than an ordinary inline, a number of things
change about how inlining is performed:

  • The inline is guarded by a CheckStructure/CheckExecutable rather than a CheckFunction.


  • Instead of propagating a constant value for the scope, we emit GetMyScope every time that the scope is needed, which loads the scope from a local variable. We do similar things for the callee.


  • The prologue of the inlined code includes SetMyScope and SetCallee nodes to eagerly plant the scope and callee into the "true call frame", i.e. the place on the stack where the call frame would have been if the call had been actually performed. This allows GetMyScope/GetCallee to work as they would if the code wasn't inlined. It also allows for trivial handling of scope and callee for call frame reconstruction upon stack introspection and during OSR.


  • A new node called GetScope is introduced, which just gets the scope of a function. This node has the expected CSE support. This allows for the SetMyScope(GetScope(@function)) sequence to set up the scope in the true call frame.


  • GetMyScope/GetCallee CSE can match against SetMyScope/SetCallee, which means that the GetMyScope/GetCallee nodes emitted during parsing are often removed during CSE, if we can prove that it is safe to do so.


  • Inlining heuristics are adjusted to grok the cost of inlining a closure. We are less likely to inline a closure call than we are to inline a normal call, since we end up emitting more code for closures due to CheckStructure, CheckExecutable, GetScope, SetMyScope, and SetCallee.


Additionally, I've fixed the VariableEventStream to ensure that we don't attempt to
plant Undefined into the true call frames. This was previously a harmless oversight,
but it becomes quite bad if OSR is relying on the scope/callee already having been
set and not subsequently clobbered by the OSR itself.

This is a ~60% speed-up on programs that frequently make calls to closures. It's
neutral on V8v7 and other major benchmark suites.

The lack of a definite speed-up is likely due the fact that closure inlining currently
does not do any cardinality [1] optimizations. We don't observe when a closure was
constructed within its caller, and so used the scope from its caller; and furthermore
we have no facility to detect when the scope is single. All scoped variable accesses
are assumed to be multiple instead. A subsequent step will be to ensure that closure
call inlining will be single and loving it.

[1] Single and loving it: Must-alias analysis for higher-order languages. Suresh

Jagannathan, Peter Thiemann, Stephen Weeks, and Andrew Wright. In POPL '98.

  • bytecode/CallLinkStatus.cpp:

(JSC::CallLinkStatus::dump):

  • bytecode/CallLinkStatus.h:

(JSC::CallLinkStatus::isClosureCall):
(CallLinkStatus):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::globalObjectFor):
(JSC):

  • bytecode/CodeBlock.h:

(CodeBlock):

  • bytecode/CodeOrigin.cpp:

(JSC::InlineCallFrame::dump):

  • dfg/DFGAbstractState.cpp:

(JSC::DFG::AbstractState::execute):

  • dfg/DFGByteCodeParser.cpp:

(ByteCodeParser):
(JSC::DFG::ByteCodeParser::handleCall):
(JSC::DFG::ByteCodeParser::emitFunctionChecks):
(JSC::DFG::ByteCodeParser::handleInlining):

  • dfg/DFGCSEPhase.cpp:

(JSC::DFG::CSEPhase::pureCSE):
(CSEPhase):
(JSC::DFG::CSEPhase::getCalleeLoadElimination):
(JSC::DFG::CSEPhase::checkExecutableElimination):
(JSC::DFG::CSEPhase::getMyScopeLoadElimination):
(JSC::DFG::CSEPhase::performNodeCSE):

  • dfg/DFGCapabilities.cpp:

(JSC::DFG::mightInlineFunctionForClosureCall):

  • dfg/DFGCapabilities.h:

(DFG):
(JSC::DFG::mightInlineFunctionForClosureCall):
(JSC::DFG::canInlineFunctionForClosureCall):
(JSC::DFG::canInlineFunctionFor):

  • dfg/DFGNode.h:

(Node):
(JSC::DFG::Node::hasExecutable):
(JSC::DFG::Node::executable):

  • dfg/DFGNodeType.h:

(DFG):

  • dfg/DFGPredictionPropagationPhase.cpp:

(JSC::DFG::PredictionPropagationPhase::propagate):

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGVariableEventStream.cpp:

(JSC::DFG::VariableEventStream::reconstruct):

  • runtime/Options.h:

(JSC):

Location:
trunk/Source/JavaScriptCore
Files:
18 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r138915 r138921  
     12013-01-05  Filip Pizlo  <fpizlo@apple.com>
     2
     3        DFG should inline closure calls
     4        https://bugs.webkit.org/show_bug.cgi?id=106067
     5
     6        Reviewed by Gavin Barraclough.
     7       
     8        This adds initial support for inlining closure calls to the DFG. A call is considered
     9        to be a closure call when the JSFunction* varies, but always has the same executable.
     10        We already have closure call inline caching in both JITs, which works by checking that
     11        the callee has an expected structure (as a cheap way of detecting that it is in fact
     12        a JSFunction) and an expected executable. Closure call inlining uses profiling data
     13        aggregated by CallLinkStatus to decide when to specialize the call to the particular
     14        structure/executable, and inline the call rather than emitting a call sequence. When
     15        we choose to do a closure inline rather than an ordinary inline, a number of things
     16        change about how inlining is performed:
     17       
     18        - The inline is guarded by a CheckStructure/CheckExecutable rather than a
     19          CheckFunction.
     20       
     21        - Instead of propagating a constant value for the scope, we emit GetMyScope every time
     22          that the scope is needed, which loads the scope from a local variable. We do similar
     23          things for the callee.
     24       
     25        - The prologue of the inlined code includes SetMyScope and SetCallee nodes to eagerly
     26          plant the scope and callee into the "true call frame", i.e. the place on the stack
     27          where the call frame would have been if the call had been actually performed. This
     28          allows GetMyScope/GetCallee to work as they would if the code wasn't inlined. It
     29          also allows for trivial handling of scope and callee for call frame reconstruction
     30          upon stack introspection and during OSR.
     31       
     32        - A new node called GetScope is introduced, which just gets the scope of a function.
     33          This node has the expected CSE support. This allows for the
     34          SetMyScope(GetScope(@function)) sequence to set up the scope in the true call frame.
     35       
     36        - GetMyScope/GetCallee CSE can match against SetMyScope/SetCallee, which means that
     37          the GetMyScope/GetCallee nodes emitted during parsing are often removed during CSE,
     38          if we can prove that it is safe to do so.
     39       
     40        - Inlining heuristics are adjusted to grok the cost of inlining a closure. We are
     41          less likely to inline a closure call than we are to inline a normal call, since we
     42          end up emitting more code for closures due to CheckStructure, CheckExecutable,
     43          GetScope, SetMyScope, and SetCallee.
     44       
     45        Additionally, I've fixed the VariableEventStream to ensure that we don't attempt to
     46        plant Undefined into the true call frames. This was previously a harmless oversight,
     47        but it becomes quite bad if OSR is relying on the scope/callee already having been
     48        set and not subsequently clobbered by the OSR itself.
     49       
     50        This is a ~60% speed-up on programs that frequently make calls to closures. It's
     51        neutral on V8v7 and other major benchmark suites.
     52       
     53        The lack of a definite speed-up is likely due the fact that closure inlining currently
     54        does not do any cardinality [1] optimizations. We don't observe when a closure was
     55        constructed within its caller, and so used the scope from its caller; and furthermore
     56        we have no facility to detect when the scope is single. All scoped variable accesses
     57        are assumed to be multiple instead. A subsequent step will be to ensure that closure
     58        call inlining will be single and loving it.
     59       
     60        [1] Single and loving it: Must-alias analysis for higher-order languages. Suresh
     61            Jagannathan, Peter Thiemann, Stephen Weeks, and Andrew Wright. In POPL '98.
     62
     63        * bytecode/CallLinkStatus.cpp:
     64        (JSC::CallLinkStatus::dump):
     65        * bytecode/CallLinkStatus.h:
     66        (JSC::CallLinkStatus::isClosureCall):
     67        (CallLinkStatus):
     68        * bytecode/CodeBlock.cpp:
     69        (JSC::CodeBlock::globalObjectFor):
     70        (JSC):
     71        * bytecode/CodeBlock.h:
     72        (CodeBlock):
     73        * bytecode/CodeOrigin.cpp:
     74        (JSC::InlineCallFrame::dump):
     75        * dfg/DFGAbstractState.cpp:
     76        (JSC::DFG::AbstractState::execute):
     77        * dfg/DFGByteCodeParser.cpp:
     78        (ByteCodeParser):
     79        (JSC::DFG::ByteCodeParser::handleCall):
     80        (JSC::DFG::ByteCodeParser::emitFunctionChecks):
     81        (JSC::DFG::ByteCodeParser::handleInlining):
     82        * dfg/DFGCSEPhase.cpp:
     83        (JSC::DFG::CSEPhase::pureCSE):
     84        (CSEPhase):
     85        (JSC::DFG::CSEPhase::getCalleeLoadElimination):
     86        (JSC::DFG::CSEPhase::checkExecutableElimination):
     87        (JSC::DFG::CSEPhase::getMyScopeLoadElimination):
     88        (JSC::DFG::CSEPhase::performNodeCSE):
     89        * dfg/DFGCapabilities.cpp:
     90        (JSC::DFG::mightInlineFunctionForClosureCall):
     91        * dfg/DFGCapabilities.h:
     92        (DFG):
     93        (JSC::DFG::mightInlineFunctionForClosureCall):
     94        (JSC::DFG::canInlineFunctionForClosureCall):
     95        (JSC::DFG::canInlineFunctionFor):
     96        * dfg/DFGNode.h:
     97        (Node):
     98        (JSC::DFG::Node::hasExecutable):
     99        (JSC::DFG::Node::executable):
     100        * dfg/DFGNodeType.h:
     101        (DFG):
     102        * dfg/DFGPredictionPropagationPhase.cpp:
     103        (JSC::DFG::PredictionPropagationPhase::propagate):
     104        * dfg/DFGSpeculativeJIT32_64.cpp:
     105        (JSC::DFG::SpeculativeJIT::compile):
     106        * dfg/DFGSpeculativeJIT64.cpp:
     107        (JSC::DFG::SpeculativeJIT::compile):
     108        * dfg/DFGVariableEventStream.cpp:
     109        (JSC::DFG::VariableEventStream::reconstruct):
     110        * runtime/Options.h:
     111        (JSC):
     112
    11132013-01-05  Filip Pizlo  <fpizlo@apple.com>
    2114
  • trunk/Source/JavaScriptCore/bytecode/CallLinkStatus.cpp

    r138871 r138921  
    120120}
    121121
    122 void CallLinkStatus::dump(PrintStream& out)
     122void CallLinkStatus::dump(PrintStream& out) const
    123123{
    124124    if (!isSet()) {
  • trunk/Source/JavaScriptCore/bytecode/CallLinkStatus.h

    r138871 r138921  
    105105   
    106106    bool couldTakeSlowPath() const { return m_couldTakeSlowPath; }
    107     bool isClosureCall() const { return !m_callTarget; }
     107    bool isClosureCall() const { return m_executable && !m_callTarget; }
    108108   
    109109    JSValue callTarget() const { return m_callTarget; }
     
    116116    bool canOptimize() const { return (m_callTarget || m_executable) && !m_couldTakeSlowPath; }
    117117   
    118     void dump(PrintStream&);
     118    void dump(PrintStream&) const;
    119119   
    120120private:
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp

    r138741 r138921  
    29342934#endif
    29352935
     2936JSGlobalObject* CodeBlock::globalObjectFor(CodeOrigin codeOrigin)
     2937{
     2938    if (!codeOrigin.inlineCallFrame)
     2939        return globalObject();
     2940    return jsCast<FunctionExecutable*>(codeOrigin.inlineCallFrame->executable.get())->generatedBytecode().globalObject();
     2941}
     2942
    29362943unsigned CodeBlock::reoptimizationRetryCounter() const
    29372944{
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.h

    r138074 r138921  
    937937        JSGlobalObject* globalObject() { return m_globalObject.get(); }
    938938       
    939         JSGlobalObject* globalObjectFor(CodeOrigin codeOrigin)
    940         {
    941             if (!codeOrigin.inlineCallFrame)
    942                 return globalObject();
    943             // FIXME: if we ever inline based on executable not function, this code will need to change.
    944             return codeOrigin.inlineCallFrame->callee->scope()->globalObject();
    945         }
     939        JSGlobalObject* globalObjectFor(CodeOrigin);
    946940
    947941        // Jump Tables
  • trunk/Source/JavaScriptCore/bytecode/CodeOrigin.cpp

    r138669 r138921  
    8787    else
    8888        out.print(", closure call");
     89    out.print(", stack >= r", stackOffset);
    8990    out.print(">");
    9091}
  • trunk/Source/JavaScriptCore/dfg/DFGAbstractState.cpp

    r137699 r138921  
    13831383        forNode(nodeIndex).set(SpecFunction);
    13841384        break;
    1385            
     1385       
     1386    case SetCallee:
     1387    case SetMyScope:
     1388        node.setCanExit(false);
     1389        break;
     1390           
     1391    case GetScope: // FIXME: We could get rid of these if we know that the JSFunction is a constant. https://bugs.webkit.org/show_bug.cgi?id=106202
    13861392    case GetMyScope:
    13871393    case SkipTopScope:
     
    14561462        forNode(nodeIndex).set(SpecInt32);
    14571463        break;
     1464       
     1465    case CheckExecutable: {
     1466        // FIXME: We could track executables in AbstractValue, which would allow us to get rid of these checks
     1467        // more thoroughly. https://bugs.webkit.org/show_bug.cgi?id=106200
     1468        // FIXME: We could eliminate these entirely if we know the exact value that flows into this.
     1469        // https://bugs.webkit.org/show_bug.cgi?id=106201
     1470        forNode(node.child1()).filter(SpecCell);
     1471        node.setCanExit(true);
     1472        break;
     1473    }
    14581474
    14591475    case CheckStructure:
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r138871 r138921  
    160160    // Handle calls. This resolves issues surrounding inlining and intrinsics.
    161161    void handleCall(Interpreter*, Instruction* currentInstruction, NodeType op, CodeSpecializationKind);
    162     void emitFunctionCheck(JSFunction* expectedFunction, NodeIndex callTarget, int registerOffset, CodeSpecializationKind);
     162    void emitFunctionChecks(const CallLinkStatus&, NodeIndex callTarget, int registerOffset, CodeSpecializationKind);
    163163    // Handle inlining. Return true if it succeeded, false if we need to plant a call.
    164     bool handleInlining(bool usesResult, NodeIndex callTargetNodeIndex, int resultOperand, bool certainAboutExpectedFunction, JSFunction*, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, CodeSpecializationKind);
     164    bool handleInlining(bool usesResult, NodeIndex callTargetNodeIndex, int resultOperand, const CallLinkStatus&, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, CodeSpecializationKind);
    165165    // Handle setting the result of an intrinsic.
    166166    void setIntrinsicResult(bool usesResult, int resultOperand, NodeIndex);
     
    13301330    }
    13311331       
    1332     JSFunction* expectedFunction = callLinkStatus.function();
    1333     if (!expectedFunction) {
    1334         // For now we have no way of reasoning about what it means to not have a specific function. This will
    1335         // change soon, though.
    1336        
    1337         addCall(interpreter, currentInstruction, op);
    1338         return;
    1339     }
    1340        
    13411332    Intrinsic intrinsic = callLinkStatus.intrinsicFor(kind);
    13421333    if (intrinsic != NoIntrinsic) {
    1343         if (!callLinkStatus.isProved())
    1344             emitFunctionCheck(expectedFunction, callTarget, registerOffset, kind);
     1334        emitFunctionChecks(callLinkStatus, callTarget, registerOffset, kind);
    13451335           
    13461336        if (handleIntrinsic(usesResult, resultOperand, intrinsic, registerOffset, argumentCountIncludingThis, prediction)) {
     
    13511341                addToGraph(Phantom, callTarget);
    13521342            }
    1353                
     1343           
    13541344            return;
    13551345        }
    1356     } else if (handleInlining(usesResult, callTarget, resultOperand, callLinkStatus.isProved(), expectedFunction, registerOffset, argumentCountIncludingThis, nextOffset, kind))
     1346    } else if (handleInlining(usesResult, callTarget, resultOperand, callLinkStatus, registerOffset, argumentCountIncludingThis, nextOffset, kind))
    13571347        return;
    13581348   
     
    13601350}
    13611351
    1362 void ByteCodeParser::emitFunctionCheck(JSFunction* expectedFunction, NodeIndex callTarget, int registerOffset, CodeSpecializationKind kind)
     1352void ByteCodeParser::emitFunctionChecks(const CallLinkStatus& callLinkStatus, NodeIndex callTarget, int registerOffset, CodeSpecializationKind kind)
    13631353{
     1354    if (callLinkStatus.isProved())
     1355        return;
     1356   
     1357    ASSERT(callLinkStatus.canOptimize());
     1358   
    13641359    NodeIndex thisArgument;
    13651360    if (kind == CodeForCall)
     
    13671362    else
    13681363        thisArgument = NoNode;
    1369     addToGraph(CheckFunction, OpInfo(expectedFunction), callTarget, thisArgument);
     1364
     1365    if (JSFunction* function = callLinkStatus.function())
     1366        addToGraph(CheckFunction, OpInfo(function), callTarget, thisArgument);
     1367    else {
     1368        ASSERT(callLinkStatus.structure());
     1369        ASSERT(callLinkStatus.executable());
     1370       
     1371        addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(callLinkStatus.structure())), callTarget);
     1372        addToGraph(CheckExecutable, OpInfo(callLinkStatus.executable()), callTarget, thisArgument);
     1373    }
    13701374}
    13711375
    1372 bool ByteCodeParser::handleInlining(bool usesResult, NodeIndex callTargetNodeIndex, int resultOperand, bool certainAboutExpectedFunction, JSFunction* expectedFunction, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, CodeSpecializationKind kind)
     1376bool ByteCodeParser::handleInlining(bool usesResult, NodeIndex callTargetNodeIndex, int resultOperand, const CallLinkStatus& callLinkStatus, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, CodeSpecializationKind kind)
    13731377{
    13741378    // First, the really simple checks: do we have an actual JS function?
    1375     if (!expectedFunction)
     1379    if (!callLinkStatus.executable())
    13761380        return false;
    1377     if (expectedFunction->isHostFunction())
     1381    if (callLinkStatus.executable()->isHostFunction())
    13781382        return false;
    13791383   
    1380     FunctionExecutable* executable = expectedFunction->jsExecutable();
     1384    FunctionExecutable* executable = jsCast<FunctionExecutable*>(callLinkStatus.executable());
    13811385   
    13821386    // Does the number of arguments we're passing match the arity of the target? We currently
     
    14071411    if (!codeBlock)
    14081412        return false;
    1409     if (!canInlineFunctionFor(codeBlock, kind))
     1413    if (!canInlineFunctionFor(codeBlock, kind, callLinkStatus.isClosureCall()))
    14101414        return false;
    14111415   
     
    14171421    // by checking the callee (if necessary) and making sure that arguments and the callee
    14181422    // are flushed.
    1419     if (!certainAboutExpectedFunction)
    1420         emitFunctionCheck(expectedFunction, callTargetNodeIndex, registerOffset, kind);
     1423    emitFunctionChecks(callLinkStatus, callTargetNodeIndex, registerOffset, kind);
    14211424   
    14221425    // FIXME: Don't flush constants!
     
    14401443    InlineStackEntry inlineStackEntry(
    14411444        this, codeBlock, codeBlock, m_graph.m_blocks.size() - 1,
    1442         expectedFunction, (VirtualRegister)m_inlineStackTop->remapOperand(
     1445        callLinkStatus.function(), (VirtualRegister)m_inlineStackTop->remapOperand(
    14431446            usesResult ? resultOperand : InvalidVirtualRegister),
    14441447        (VirtualRegister)inlineCallFrameStart, argumentCountIncludingThis, kind);
     
    14511454
    14521455    addToGraph(InlineStart, OpInfo(argumentPositionStart));
     1456    if (callLinkStatus.isClosureCall()) {
     1457        addToGraph(SetCallee, callTargetNodeIndex);
     1458        addToGraph(SetMyScope, addToGraph(GetScope, callTargetNodeIndex));
     1459    }
    14531460   
    14541461    parseCodeBlock();
  • trunk/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp

    r138669 r138921  
    9090    }
    9191
    92     enum InlineCallFrameRequirement { RequireSameInlineCallFrame, DoNotCareAboutInlineCallFrame };
    93     template<InlineCallFrameRequirement inlineCallFrameRequirement>
    94     NodeIndex genericPureCSE(Node& node)
     92    NodeIndex pureCSE(Node& node)
    9593    {
    9694        NodeIndex child1 = canonicalize(node.child1());
     
    111109           
    112110            if (node.arithNodeFlags() != otherNode.arithNodeFlags())
    113                 continue;
    114            
    115             if (inlineCallFrameRequirement == RequireSameInlineCallFrame
    116                 && node.codeOrigin.inlineCallFrame != otherNode.codeOrigin.inlineCallFrame)
    117111                continue;
    118112           
     
    140134    }
    141135   
    142     NodeIndex pureCSE(Node& node)
    143     {
    144         return genericPureCSE<DoNotCareAboutInlineCallFrame>(node);
    145     }
    146    
    147     NodeIndex pureCSERequiringSameInlineCallFrame(Node& node)
    148     {
    149         return genericPureCSE<RequireSameInlineCallFrame>(node);
    150     }
    151    
    152136    NodeIndex constantCSE(Node& node)
    153137    {
     
    178162           
    179163            return index;
     164        }
     165        return NoNode;
     166    }
     167   
     168    NodeIndex getCalleeLoadElimination(InlineCallFrame* inlineCallFrame)
     169    {
     170        for (unsigned i = m_indexInBlock; i--;) {
     171            NodeIndex index = m_currentBlock->at(i);
     172            Node& node = m_graph[index];
     173            if (!node.shouldGenerate())
     174                continue;
     175            if (node.codeOrigin.inlineCallFrame != inlineCallFrame)
     176                continue;
     177            switch (node.op()) {
     178            case GetCallee:
     179                return index;
     180            case SetCallee:
     181                return node.child1().index();
     182            default:
     183                break;
     184            }
    180185        }
    181186        return NoNode;
     
    413418            Node& node = m_graph[index];
    414419            if (node.op() == CheckFunction && node.child1() == child1 && node.function() == function)
     420                return true;
     421        }
     422        return false;
     423    }
     424   
     425    bool checkExecutableElimination(ExecutableBase* executable, NodeIndex child1)
     426    {
     427        for (unsigned i = endIndexForPureCSE(); i--;) {
     428            NodeIndex index = m_currentBlock->at(i);
     429            if (index == child1)
     430                break;
     431
     432            Node& node = m_graph[index];
     433            if (node.op() == CheckExecutable && node.child1() == child1 && node.executable() == executable)
    415434                return true;
    416435        }
     
    831850            case GetMyScope:
    832851                return index;
     852            case SetMyScope:
     853                return node.child1().index();
    833854            default:
    834855                break;
     
    11451166        case SkipScope:
    11461167        case GetScopeRegisters:
     1168        case GetScope:
    11471169            setReplacement(pureCSE(node));
    11481170            break;
    11491171           
    11501172        case GetCallee:
    1151             setReplacement(pureCSERequiringSameInlineCallFrame(node));
     1173            setReplacement(getCalleeLoadElimination(node.codeOrigin.inlineCallFrame));
    11521174            break;
    11531175
     
    13521374            break;
    13531375               
     1376        case CheckExecutable:
     1377            if (checkExecutableElimination(node.executable(), node.child1().index()))
     1378                eliminate();
     1379            break;
     1380               
    13541381        case CheckArray:
    13551382            if (checkArrayElimination(node.child1().index(), node.arrayMode()))
  • trunk/Source/JavaScriptCore/dfg/DFGCapabilities.cpp

    r137094 r138921  
    5454{
    5555    return codeBlock->instructionCount() <= Options::maximumFunctionForCallInlineCandidateInstructionCount()
     56        && !codeBlock->ownerExecutable()->needsActivation();
     57}
     58bool mightInlineFunctionForClosureCall(CodeBlock* codeBlock)
     59{
     60    return codeBlock->instructionCount() <= Options::maximumFunctionForClosureCallInlineCandidateInstructionCount()
    5661        && !codeBlock->ownerExecutable()->needsActivation();
    5762}
  • trunk/Source/JavaScriptCore/dfg/DFGCapabilities.h

    r138074 r138921  
    4545bool mightCompileFunctionForConstruct(CodeBlock*);
    4646bool mightInlineFunctionForCall(CodeBlock*);
     47bool mightInlineFunctionForClosureCall(CodeBlock*);
    4748bool mightInlineFunctionForConstruct(CodeBlock*);
    4849
     
    273274inline bool mightCompileFunctionForConstruct(CodeBlock*) { return false; }
    274275inline bool mightInlineFunctionForCall(CodeBlock*) { return false; }
     276inline bool mightInlineFunctionForClosureCall(CodeBlock*) { return false; }
    275277inline bool mightInlineFunctionForConstruct(CodeBlock*) { return false; }
    276278
     
    318320}
    319321
     322inline bool canInlineFunctionForClosureCall(CodeBlock* codeBlock)
     323{
     324    return mightInlineFunctionForClosureCall(codeBlock) && canInlineOpcodes(codeBlock);
     325}
     326
    320327inline bool canInlineFunctionForConstruct(CodeBlock* codeBlock)
    321328{
     
    331338}
    332339
    333 inline bool canInlineFunctionFor(CodeBlock* codeBlock, CodeSpecializationKind kind)
    334 {
     340inline bool canInlineFunctionFor(CodeBlock* codeBlock, CodeSpecializationKind kind, bool isClosureCall)
     341{
     342    if (isClosureCall) {
     343        ASSERT(kind == CodeForCall);
     344        return canInlineFunctionForClosureCall(codeBlock);
     345    }
    335346    if (kind == CodeForCall)
    336347        return canInlineFunctionForCall(codeBlock);
  • trunk/Source/JavaScriptCore/dfg/DFGNode.h

    r136601 r138921  
    703703        return result;
    704704    }
     705   
     706    bool hasExecutable()
     707    {
     708        return op() == CheckExecutable;
     709    }
     710   
     711    ExecutableBase* executable()
     712    {
     713        return jsCast<ExecutableBase*>(reinterpret_cast<JSCell*>(m_opInfo));
     714    }
    705715
    706716    bool hasStructureTransitionData()
  • trunk/Source/JavaScriptCore/dfg/DFGNodeType.h

    r137699 r138921  
    5353    macro(CreateThis, NodeResultJS) /* Note this is not MustGenerate since we're returning it anyway. */ \
    5454    macro(GetCallee, NodeResultJS) \
     55    macro(SetCallee, NodeMustGenerate) \
    5556    \
    5657    /* Nodes for local variable access. These nodes are linked together using Phi nodes. */\
     
    125126    macro(PutByIdDirect, NodeMustGenerate | NodeClobbersWorld) \
    126127    macro(CheckStructure, NodeMustGenerate) \
     128    macro(CheckExecutable, NodeMustGenerate) \
    127129    macro(ForwardCheckStructure, NodeMustGenerate) \
    128130    /* Transition watchpoints are a contract between the party setting the watchpoint */\
     
    151153    macro(PutByOffset, NodeMustGenerate) \
    152154    macro(GetArrayLength, NodeResultInt32) \
     155    macro(GetScope, NodeResultJS) \
    153156    macro(GetMyScope, NodeResultJS) \
     157    macro(SetMyScope, NodeMustGenerate) \
    154158    macro(SkipTopScope, NodeResultJS) \
    155159    macro(SkipScope, NodeResultJS) \
  • trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp

    r137980 r138921  
    768768            break;
    769769
     770        case SetCallee:
     771        case SetMyScope:
     772            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
     773            break;
     774           
     775        case GetScope:
     776            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
     777            changed |= setPrediction(SpecCellOther);
     778            break;
     779
    770780#ifndef NDEBUG
    771781        // These get ignored because they don't return anything.
     
    778788        case SetArgument:
    779789        case CheckStructure:
     790        case CheckExecutable:
    780791        case ForwardCheckStructure:
    781792        case StructureTransitionWatchpoint:
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp

    r138871 r138921  
    39703970    }
    39713971       
     3972    case SetCallee: {
     3973        SpeculateCellOperand callee(this, node.child1());
     3974        m_jit.storePtr(callee.gpr(), JITCompiler::payloadFor(static_cast<VirtualRegister>(node.codeOrigin.stackOffset() + static_cast<int>(JSStack::Callee))));
     3975        noResult(m_compileIndex);
     3976        break;
     3977    }
     3978       
     3979    case GetScope: {
     3980        SpeculateCellOperand function(this, node.child1());
     3981        GPRTemporary result(this, function);
     3982        m_jit.loadPtr(JITCompiler::Address(function.gpr(), JSFunction::offsetOfScopeChain()), result.gpr());
     3983        cellResult(result.gpr(), m_compileIndex);
     3984        break;
     3985    }
     3986       
    39723987    case GetMyScope: {
    39733988        GPRTemporary result(this);
     
    39763991        m_jit.loadPtr(JITCompiler::payloadFor(static_cast<VirtualRegister>(node.codeOrigin.stackOffset() + static_cast<int>(JSStack::ScopeChain))), resultGPR);
    39773992        cellResult(resultGPR, m_compileIndex);
     3993        break;
     3994    }
     3995       
     3996    case SetMyScope: {
     3997        SpeculateCellOperand callee(this, node.child1());
     3998        m_jit.storePtr(callee.gpr(), JITCompiler::payloadFor(static_cast<VirtualRegister>(node.codeOrigin.stackOffset() + static_cast<int>(JSStack::ScopeChain))));
     3999        noResult(m_compileIndex);
    39784000        break;
    39794001    }
     
    41434165    }
    41444166
     4167    case CheckExecutable: {
     4168        SpeculateCellOperand function(this, node.child1());
     4169        speculationCheck(BadExecutable, JSValueSource::unboxedCell(function.gpr()), node.child1(), m_jit.branchWeakPtr(JITCompiler::NotEqual, JITCompiler::Address(function.gpr(), JSFunction::offsetOfExecutable()), node.executable()));
     4170        noResult(m_compileIndex);
     4171        break;
     4172    }
     4173       
    41454174    case CheckStructure:
    41464175    case ForwardCheckStructure: {
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

    r138871 r138921  
    39223922    }
    39233923       
     3924    case SetCallee: {
     3925        SpeculateCellOperand callee(this, node.child1());
     3926        m_jit.storePtr(callee.gpr(), JITCompiler::addressFor(static_cast<VirtualRegister>(node.codeOrigin.stackOffset() + static_cast<int>(JSStack::Callee))));
     3927        noResult(m_compileIndex);
     3928        break;
     3929    }
     3930       
     3931    case GetScope: {
     3932        SpeculateCellOperand function(this, node.child1());
     3933        GPRTemporary result(this, function);
     3934        m_jit.loadPtr(JITCompiler::Address(function.gpr(), JSFunction::offsetOfScopeChain()), result.gpr());
     3935        cellResult(result.gpr(), m_compileIndex);
     3936        break;
     3937    }
     3938       
    39243939    case GetMyScope: {
    39253940        GPRTemporary result(this);
     
    39283943        m_jit.loadPtr(JITCompiler::addressFor(static_cast<VirtualRegister>(node.codeOrigin.stackOffset() + static_cast<int>(JSStack::ScopeChain))), resultGPR);
    39293944        cellResult(resultGPR, m_compileIndex);
     3945        break;
     3946    }
     3947       
     3948    case SetMyScope: {
     3949        SpeculateCellOperand callee(this, node.child1());
     3950        m_jit.storePtr(callee.gpr(), JITCompiler::addressFor(static_cast<VirtualRegister>(node.codeOrigin.stackOffset() + static_cast<int>(JSStack::ScopeChain))));
     3951        noResult(m_compileIndex);
    39303952        break;
    39313953    }
     
    40814103        break;
    40824104    }
     4105       
     4106    case CheckExecutable: {
     4107        SpeculateCellOperand function(this, node.child1());
     4108        speculationCheck(BadExecutable, JSValueRegs(function.gpr()), node.child1(), m_jit.branchWeakPtr(JITCompiler::NotEqual, JITCompiler::Address(function.gpr(), JSFunction::offsetOfExecutable()), node.executable()));
     4109        noResult(m_compileIndex);
     4110        break;
     4111    }
     4112       
    40834113    case CheckStructure:
    40844114    case ForwardCheckStructure: {
  • trunk/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp

    r135469 r138921  
    283283            ValueRecovery::displacedInJSStack(static_cast<VirtualRegister>(info->u.virtualReg), info->format);
    284284    }
     285   
     286    // Step 5: Make sure that for locals that coincide with true call frame headers, the exit compiler knows
     287    // that those values don't have to be recovered. Signal this by using ValueRecovery::alreadyInJSStack()
     288    for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame; inlineCallFrame = inlineCallFrame->caller.inlineCallFrame) {
     289        for (unsigned i = JSStack::CallFrameHeaderSize; i--;)
     290            valueRecoveries.setLocal(inlineCallFrame->stackOffset - i - 1, ValueRecovery::alreadyInJSStack());
     291    }
    285292}
    286293
  • trunk/Source/JavaScriptCore/runtime/Options.h

    r138074 r138921  
    8181    \
    8282    v(unsigned, maximumFunctionForCallInlineCandidateInstructionCount, 180) \
     83    v(unsigned, maximumFunctionForClosureCallInlineCandidateInstructionCount, 100) \
    8384    v(unsigned, maximumFunctionForConstructInlineCandidateInstructionCount, 100) \
    8485    \
Note: See TracChangeset for help on using the changeset viewer.