Support direct calls to intrinsic functions
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 7 Oct 2011 18:39:45 +0000 (18:39 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 7 Oct 2011 18:39:45 +0000 (18:39 +0000)
https://bugs.webkit.org/show_bug.cgi?id=69646

Reviewed by Gavin Barraclough.

Add support for optimising non-method_check calls
to intrinsic functions (eg. when Math.abs, etc are
cached in local variables).

* bytecode/CodeBlock.h:
(JSC::getCallLinkInfoBytecodeIndex):
    Support searching CallLinkInfos by bytecode index
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
    Add support for linked calls in addition to method_check
    when searching for intrinsics
* dfg/DFGNode.h:
(JSC::DFG::Node::hasFunctionCheckData):
(JSC::DFG::Node::function):
    Add ability to store a JSFunction* in a node - this is safe
    as the function will be marked by the codeblock we're compiling
* dfg/DFGPropagator.cpp:
(JSC::DFG::Propagator::propagateNodePredictions):
(JSC::DFG::Propagator::checkFunctionElimination):
(JSC::DFG::Propagator::performNodeCSE):
    Add support for new CheckFunction node, and implement CSE pass.
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
    Rather trivial implementation of CheckFunction
* jit/JIT.cpp:
(JSC::JIT::privateCompile):
* jit/JIT.h:
* jit/JITCall.cpp:
(JSC::JIT::compileOpCall):
* jit/JITCall32_64.cpp:
(JSC::JIT::compileOpCall):
    Need to propagate bytecode index for calls now.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@96962 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGPropagator.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITCall.cpp
Source/JavaScriptCore/jit/JITCall32_64.cpp

index bc56d2f..4bf2a73 100644 (file)
@@ -1,3 +1,45 @@
+2011-10-07  Oliver Hunt  <oliver@apple.com>
+
+        Support direct calls to intrinsic functions
+        https://bugs.webkit.org/show_bug.cgi?id=69646
+
+        Reviewed by Gavin Barraclough.
+
+        Add support for optimising non-method_check calls
+        to intrinsic functions (eg. when Math.abs, etc are
+        cached in local variables). 
+
+        * bytecode/CodeBlock.h:
+        (JSC::getCallLinkInfoBytecodeIndex):
+            Support searching CallLinkInfos by bytecode index
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+            Add support for linked calls in addition to method_check
+            when searching for intrinsics
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasFunctionCheckData):
+        (JSC::DFG::Node::function):
+            Add ability to store a JSFunction* in a node - this is safe
+            as the function will be marked by the codeblock we're compiling
+        * dfg/DFGPropagator.cpp:
+        (JSC::DFG::Propagator::propagateNodePredictions):
+        (JSC::DFG::Propagator::checkFunctionElimination):
+        (JSC::DFG::Propagator::performNodeCSE):
+            Add support for new CheckFunction node, and implement CSE pass.
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+            Rather trivial implementation of CheckFunction
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompile):
+        * jit/JIT.h:
+        * jit/JITCall.cpp:
+        (JSC::JIT::compileOpCall):
+        * jit/JITCall32_64.cpp:
+        (JSC::JIT::compileOpCall):
+            Need to propagate bytecode index for calls now.
+
 2011-10-07  Dominic Cooney  <dominicc@chromium.org>
 
         [JSC] Disable ThreadRestrictionVerifier for JIT ExecutableMemoryHandles
index 71c5491..cd9cd1a 100644 (file)
@@ -124,6 +124,7 @@ namespace JSC {
         bool hasSeenShouldRepatch : 1;
         bool isCall : 1;
         bool isDFG : 1;
+        unsigned bytecodeIndex;
 
         bool isLinked() { return callee; }
         void unlink(JSGlobalData&, RepatchBuffer&);
@@ -210,6 +211,11 @@ namespace JSC {
         return callLinkInfo->callReturnLocation.executableAddress();
     }
 
+    inline unsigned getCallLinkInfoBytecodeIndex(CallLinkInfo* callLinkInfo)
+    {
+        return callLinkInfo->bytecodeIndex;
+    }
+
     inline void* getMethodCallLinkInfoReturnLocation(MethodCallLinkInfo* methodCallLinkInfo)
     {
         return methodCallLinkInfo->callReturnLocation.executableAddress();
@@ -290,6 +296,11 @@ namespace JSC {
         {
             return *(binarySearch<CallLinkInfo, void*, getCallLinkInfoReturnLocation>(m_callLinkInfos.begin(), m_callLinkInfos.size(), returnAddress.value()));
         }
+        
+        CallLinkInfo& getCallLinkInfo(unsigned bytecodeIndex)
+        {
+            return *(binarySearch<CallLinkInfo, unsigned, getCallLinkInfoBytecodeIndex>(m_callLinkInfos.begin(), m_callLinkInfos.size(), bytecodeIndex));
+        }
 
         MethodCallLinkInfo& getMethodCallLinkInfo(ReturnAddressPtr returnAddress)
         {
index 65281c4..175e6f9 100644 (file)
@@ -1543,7 +1543,15 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             
         case op_call: {
             NodeIndex callTarget = get(currentInstruction[1].u.operand);
-            if (m_graph.isFunctionConstant(m_codeBlock, callTarget)) {
+            enum { ConstantFunction, LinkedFunction, UnknownFunction } callType;
+            
+            if (m_graph.isFunctionConstant(m_codeBlock, callTarget))
+                callType = ConstantFunction;
+            else if (m_profiledBlock->getCallLinkInfo(m_currentIndex).isLinked())
+                callType = LinkedFunction;
+            else
+                callType = UnknownFunction;
+            if (callType != UnknownFunction) {
                 int argCount = currentInstruction[2].u.operand;
                 int registerOffset = currentInstruction[3].u.operand;
                 int firstArg = registerOffset - argCount - RegisterFile::CallFrameHeaderSize;
@@ -1559,8 +1567,16 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                     usesResult = true;
                     prediction = getPrediction(m_graph.size(), m_currentIndex + OPCODE_LENGTH(op_call));
                 }
-                
-                DFG::Intrinsic intrinsic = m_graph.valueOfFunctionConstant(m_codeBlock, callTarget)->executable()->intrinsic();
+                DFG::Intrinsic intrinsic;
+                if (callType == ConstantFunction)
+                    intrinsic = m_graph.valueOfFunctionConstant(m_codeBlock, callTarget)->executable()->intrinsic();
+                else {
+                    ASSERT(callType == LinkedFunction);
+                    JSFunction* function = m_profiledBlock->getCallLinkInfo(m_currentIndex).callee.get();
+                    intrinsic = function->executable()->intrinsic();
+                    if (intrinsic != NoIntrinsic)
+                        addToGraph(CheckFunction, OpInfo(function), callTarget);
+                }
                 
                 if (handleIntrinsic(usesResult, resultOperand, intrinsic, firstArg, lastArg, prediction)) {
                     // NEXT_OPCODE() has to be inside braces.
index 41446f9..d00abbe 100644 (file)
@@ -341,6 +341,7 @@ static inline const char* arithNodeFlagsAsString(ArithNodeFlags flags)
     macro(PutScopedVar, NodeMustGenerate | NodeClobbersWorld) \
     macro(GetGlobalVar, NodeResultJS | NodeMustGenerate) \
     macro(PutGlobalVar, NodeMustGenerate | NodeClobbersWorld) \
+    macro(CheckFunction, NodeMustGenerate) \
     \
     /* Optimizations for array mutation. */\
     macro(ArrayPush, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
@@ -807,7 +808,18 @@ struct Node {
         ASSERT(hasMethodCheckData());
         return m_opInfo2;
     }
-    
+
+    bool hasFunctionCheckData()
+    {
+        return op == CheckFunction;
+    }
+
+    JSFunction* function()
+    {
+        ASSERT(hasFunctionCheckData());
+        return reinterpret_cast<JSFunction*>(m_opInfo);
+    }
+
     bool hasStructureTransitionData()
     {
         return op == PutStructure;
index 71fb29a..35d893f 100644 (file)
@@ -587,6 +587,7 @@ private:
         case PutById:
         case PutByIdDirect:
         case CheckStructure:
+        case CheckFunction:
         case PutStructure:
         case PutByOffset:
             break;
@@ -984,7 +985,7 @@ private:
         }
         return NoNode;
     }
-    
+
     NodeIndex getMethodLoadElimination(const MethodCheckData& methodCheckData, unsigned identifierNumber, NodeIndex child1)
     {
         NodeIndex start = startIndexForChildren(child1);
@@ -1022,7 +1023,27 @@ private:
         }
         return NoNode;
     }
-    
+
+    bool checkFunctionElimination(JSFunction* function, NodeIndex child1)
+    {
+        NodeIndex start = startIndexForChildren(child1);
+        for (NodeIndex index = m_compileIndex; index-- > start;) {
+            Node& node = m_graph[index];
+            switch (node.op) {
+            case CheckFunction:
+                if (node.child1() == child1 && node.function() == function)
+                    return true;
+                break;
+                
+            default:
+                if (clobbersWorld(index))
+                    return false;
+                break;
+            }
+        }
+        return false;
+    }
+
     bool checkStructureLoadElimination(const StructureSet& structureSet, NodeIndex child1)
     {
         NodeIndex start = startIndexForChildren(child1);
@@ -1308,7 +1329,12 @@ private:
             if (checkStructureLoadElimination(node.structureSet(), node.child1()))
                 eliminate();
             break;
-            
+
+        case CheckFunction:
+            if (checkFunctionElimination(node.function(), node.child1()))
+                eliminate();
+            break;
+
         case GetPropertyStorage:
             setReplacement(getPropertyStorageLoadElimination(node.child1()));
             break;
index c3d7d50..6103624 100644 (file)
@@ -1943,6 +1943,13 @@ void SpeculativeJIT::compile(Node& node)
         break;
     }
 
+    case CheckFunction: {
+        SpeculateCellOperand function(this, node.child1());
+        speculationCheck(m_jit.branchPtr(JITCompiler::NotEqual, function.gpr(), JITCompiler::TrustedImmPtr(node.function())));
+        noResult(m_compileIndex);
+        break;
+    }
+
     case CheckStructure: {
         SpeculateCellOperand base(this, node.child1());
         
index f736842..96ee517 100644 (file)
@@ -2007,7 +2007,12 @@ void SpeculativeJIT::compile(Node& node)
         integerResult(resultGPR, m_compileIndex);
         break;
     }
-
+    case CheckFunction: {
+        SpeculateCellOperand function(this, node.child1());
+        speculationCheck(m_jit.branchPtr(JITCompiler::NotEqual, function.gpr(), JITCompiler::TrustedImmPtr(node.function())));
+        noResult(m_compileIndex);
+        break;
+    }
     case CheckStructure: {
         SpeculateCellOperand base(this, node.child1());
         
index bc5b145..1086273 100644 (file)
@@ -678,6 +678,7 @@ JITCode JIT::privateCompile(CodePtr* functionEntryArityCheck)
     for (unsigned i = 0; i < m_codeBlock->numberOfCallLinkInfos(); ++i) {
         CallLinkInfo& info = m_codeBlock->callLinkInfo(i);
         info.isCall = m_callStructureStubCompilationInfo[i].isCall;
+        info.bytecodeIndex = m_callStructureStubCompilationInfo[i].bytecodeIndex;
         info.callReturnLocation = CodeLocationLabel(patchBuffer.locationOfNearCall(m_callStructureStubCompilationInfo[i].callReturnLocation));
         info.hotPathBegin = patchBuffer.locationOf(m_callStructureStubCompilationInfo[i].hotPathBegin);
         info.hotPathOther = patchBuffer.locationOfNearCall(m_callStructureStubCompilationInfo[i].hotPathOther);
index fc8f6d3..95f7b9c 100644 (file)
@@ -165,6 +165,7 @@ namespace JSC {
         MacroAssembler::Call hotPathOther;
         MacroAssembler::Call callReturnLocation;
         bool isCall;
+        unsigned bytecodeIndex;
     };
 
     struct MethodCallCompilationInfo {
index 7f7fcd5..c88b52e 100644 (file)
@@ -138,6 +138,7 @@ void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned ca
     m_callStructureStubCompilationInfo.append(StructureStubCompilationInfo());
     m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck;
     m_callStructureStubCompilationInfo[callLinkInfoIndex].isCall = opcodeID != op_construct;
+    m_callStructureStubCompilationInfo[callLinkInfoIndex].bytecodeIndex = m_bytecodeOffset;
 
     // The following is the fast case, only used whan a callee can be linked.
 
index 4a5ce72..ad34aa3 100644 (file)
@@ -223,6 +223,7 @@ void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned ca
     m_callStructureStubCompilationInfo.append(StructureStubCompilationInfo());
     m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck;
     m_callStructureStubCompilationInfo[callLinkInfoIndex].isCall = opcodeID != op_construct;
+    m_callStructureStubCompilationInfo[callLinkInfoIndex].bytecodeIndex = m_bytecodeIndex;
 
     addSlowCase(branch32(NotEqual, regT1, TrustedImm32(JSValue::CellTag)));