Rationalize closure call heuristics and profiling
https://bugs.webkit.org/show_bug.cgi?id=106270

Source/JavaScriptCore: 

Reviewed by Oliver Hunt.
        
Did a number of things:
        
- CallLinkInfo now remembers if it was ever a closure call, and CallLinkStatus uses
  this. Reduces the likelihood that we will inline a closure call as if it was a
  normal call.
        
- Made InlineCallFrame print inferred function names, and refactored
  CodeBlock::inferredName() to better use FunctionExecutable's API.
        
- Made bytecode dumping print frequent exit sites that led to recompilation.
        
- Made bytecode dumping for op_call and op_construct print what the CallLinkStatus
  saw.
        
* bytecode/CallLinkInfo.h:
(JSC::CallLinkInfo::CallLinkInfo):
(CallLinkInfo):
* bytecode/CallLinkStatus.cpp:
(JSC::CallLinkStatus::computeFor):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::inferredName):
(JSC::CodeBlock::dumpBytecodeCommentAndNewLine):
(JSC::CodeBlock::printCallOp):
* bytecode/CodeOrigin.cpp:
(JSC::CodeOrigin::dump):
(JSC::InlineCallFrame::inferredName):
(JSC):
(JSC::InlineCallFrame::dumpBriefFunctionInformation):
(JSC::InlineCallFrame::dump):
* bytecode/CodeOrigin.h:
(InlineCallFrame):
* bytecode/DFGExitProfile.cpp:
(JSC::DFG::ExitProfile::exitSitesFor):
(DFG):
* bytecode/DFGExitProfile.h:
(ExitProfile):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):

Source/WTF: 

Reviewed by Oliver Hunt.
        
Add a macro to add a method to a class that returns a dumper. Allows you to have
secondary dump() methods for dumping either more or less information.

* wtf/PrintStream.h:
(WTF):

Tools: 

Reviewed by Oliver Hunt.
        
Add ability to use display-profiler-output via a pipe, and add the ability to dump
all generated code ('display *' or 'd *').

* Scripts/display-profiler-output:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@139021 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index d6e68b1..6a135ff 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,49 @@
+2013-01-07  Filip Pizlo  <fpizlo@apple.com>
+
+        Rationalize closure call heuristics and profiling
+        https://bugs.webkit.org/show_bug.cgi?id=106270
+
+        Reviewed by Oliver Hunt.
+        
+        Did a number of things:
+        
+        - CallLinkInfo now remembers if it was ever a closure call, and CallLinkStatus uses
+          this. Reduces the likelihood that we will inline a closure call as if it was a
+          normal call.
+        
+        - Made InlineCallFrame print inferred function names, and refactored
+          CodeBlock::inferredName() to better use FunctionExecutable's API.
+        
+        - Made bytecode dumping print frequent exit sites that led to recompilation.
+        
+        - Made bytecode dumping for op_call and op_construct print what the CallLinkStatus
+          saw.
+        
+        * bytecode/CallLinkInfo.h:
+        (JSC::CallLinkInfo::CallLinkInfo):
+        (CallLinkInfo):
+        * bytecode/CallLinkStatus.cpp:
+        (JSC::CallLinkStatus::computeFor):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::inferredName):
+        (JSC::CodeBlock::dumpBytecodeCommentAndNewLine):
+        (JSC::CodeBlock::printCallOp):
+        * bytecode/CodeOrigin.cpp:
+        (JSC::CodeOrigin::dump):
+        (JSC::InlineCallFrame::inferredName):
+        (JSC):
+        (JSC::InlineCallFrame::dumpBriefFunctionInformation):
+        (JSC::InlineCallFrame::dump):
+        * bytecode/CodeOrigin.h:
+        (InlineCallFrame):
+        * bytecode/DFGExitProfile.cpp:
+        (JSC::DFG::ExitProfile::exitSitesFor):
+        (DFG):
+        * bytecode/DFGExitProfile.h:
+        (ExitProfile):
+        * jit/JITStubs.cpp:
+        (JSC::DEFINE_STUB_FUNCTION):
+
 2013-01-07  Ryosuke Niwa  <rniwa@webkit.org>
 
         Sorted the xcodeproj file.
diff --git a/Source/JavaScriptCore/bytecode/CallLinkInfo.h b/Source/JavaScriptCore/bytecode/CallLinkInfo.h
index 5760843..cdc77ed 100644
--- a/Source/JavaScriptCore/bytecode/CallLinkInfo.h
+++ b/Source/JavaScriptCore/bytecode/CallLinkInfo.h
@@ -57,6 +57,7 @@
     CallLinkInfo()
         : hasSeenShouldRepatch(false)
         , isDFG(false)
+        , hasSeenClosure(false)
         , callType(None)
     {
     }
@@ -80,7 +81,8 @@
     RefPtr<ClosureCallStubRoutine> stub;
     bool hasSeenShouldRepatch : 1;
     bool isDFG : 1;
-    CallType callType : 6;
+    bool hasSeenClosure : 1;
+    CallType callType : 5;
     unsigned calleeGPR : 8;
     CodeOrigin codeOrigin;
 
diff --git a/Source/JavaScriptCore/bytecode/CallLinkStatus.cpp b/Source/JavaScriptCore/bytecode/CallLinkStatus.cpp
index 00a07f7..c90f9c7 100644
--- a/Source/JavaScriptCore/bytecode/CallLinkStatus.cpp
+++ b/Source/JavaScriptCore/bytecode/CallLinkStatus.cpp
@@ -112,6 +112,9 @@
     JSFunction* target = callLinkInfo.lastSeenCallee.get();
     if (!target)
         return computeFromLLInt(profiledBlock, bytecodeIndex);
+    
+    if (callLinkInfo.hasSeenClosure)
+        return CallLinkStatus(target->executable(), target->structure());
 
     return CallLinkStatus(target);
 #else
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index a2d507f..f54d7e2 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -31,6 +31,7 @@
 #include "CodeBlock.h"
 
 #include "BytecodeGenerator.h"
+#include "CallLinkStatus.h"
 #include "DFGCapabilities.h"
 #include "DFGCommon.h"
 #include "DFGNode.h"
@@ -48,6 +49,7 @@
 #include "RepatchBuffer.h"
 #include "SlotVisitorInlines.h"
 #include <stdio.h>
+#include <wtf/CommaPrinter.h>
 #include <wtf/StringExtras.h>
 #include <wtf/StringPrintStream.h>
 #include <wtf/UnusedParam.h>
@@ -72,7 +74,7 @@
     case EvalCode:
         return "<eval>";
     case FunctionCode:
-        return jsCast<FunctionExecutable*>(ownerExecutable())->unlinkedExecutable()->inferredName().string();
+        return jsCast<FunctionExecutable*>(ownerExecutable())->inferredName().string();
     default:
         CRASH();
         return String();
@@ -155,6 +157,15 @@
 
 void CodeBlock::dumpBytecodeCommentAndNewLine(PrintStream& out, int location)
 {
+#if ENABLE(DFG_JIT)
+    Vector<FrequentExitSite> exitSites = exitProfile().exitSitesFor(location);
+    if (!exitSites.isEmpty()) {
+        out.print(" !! frequent exits: ");
+        CommaPrinter comma;
+        for (unsigned i = 0; i < exitSites.size(); ++i)
+            out.print(comma, exitSites[i].kind());
+    }
+#endif // ENABLE(DFG_JIT)
 #if ENABLE(BYTECODE_COMMENTS)
     const char* comment = commentForBytecodeOffset(location);
     if (comment)
@@ -473,6 +484,7 @@
                 out.printf(" jit(%p, exec %p)", target, target->executable());
         }
 #endif
+        out.print(" status(", CallLinkStatus::computeFor(this, location), ")");
     }
     it += 2;
 }
diff --git a/Source/JavaScriptCore/bytecode/CodeOrigin.cpp b/Source/JavaScriptCore/bytecode/CodeOrigin.cpp
index 324acca..a628844 100644
--- a/Source/JavaScriptCore/bytecode/CodeOrigin.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeOrigin.cpp
@@ -62,8 +62,11 @@
         if (i)
             out.print(" --> ");
         
-        if (InlineCallFrame* frame = stack[i].inlineCallFrame)
-            out.print("#", frame->hash(), ":<", RawPointer(frame->executable.get()), "> ");
+        if (InlineCallFrame* frame = stack[i].inlineCallFrame) {
+            out.print(frame->briefFunctionInformation(), ":<", RawPointer(frame->executable.get()), "> ");
+            if (frame->isClosureCall())
+                out.print("(closure) ");
+        }
         
         out.print("bc#", stack[i].bytecodeIndex);
     }
@@ -74,18 +77,29 @@
     return executable->hashFor(specializationKind());
 }
 
+String InlineCallFrame::inferredName() const
+{
+    return jsCast<FunctionExecutable*>(executable.get())->inferredName().string();
+}
+
 CodeBlock* InlineCallFrame::baselineCodeBlock() const
 {
     return jsCast<FunctionExecutable*>(executable.get())->baselineCodeBlockFor(specializationKind());
 }
 
+void InlineCallFrame::dumpBriefFunctionInformation(PrintStream& out) const
+{
+    out.print(inferredName(), "#", hash());
+}
+
 void InlineCallFrame::dump(PrintStream& out) const
 {
-    out.print("#", hash(), ":<", RawPointer(executable.get()), ", bc#", caller.bytecodeIndex, ", ", specializationKind());
+    out.print(briefFunctionInformation(), ":<", RawPointer(executable.get()), ", bc#", caller.bytecodeIndex, ", ", specializationKind());
     if (callee)
         out.print(", known callee: ", JSValue(callee.get()));
     else
         out.print(", closure call");
+    out.print(", numArgs+this = ", arguments.size());
     out.print(", stack >= r", stackOffset);
     out.print(">");
 }
diff --git a/Source/JavaScriptCore/bytecode/CodeOrigin.h b/Source/JavaScriptCore/bytecode/CodeOrigin.h
index 2a9ce0c..9ab5fca 100644
--- a/Source/JavaScriptCore/bytecode/CodeOrigin.h
+++ b/Source/JavaScriptCore/bytecode/CodeOrigin.h
@@ -110,11 +110,15 @@
     
     bool isClosureCall() const { return !callee; }
     
+    String inferredName() const;
     CodeBlockHash hash() const;
     
     CodeBlock* baselineCodeBlock() const;
     
+    void dumpBriefFunctionInformation(PrintStream&) const;
     void dump(PrintStream&) const;
+
+    MAKE_PRINT_METHOD(InlineCallFrame, dumpBriefFunctionInformation, briefFunctionInformation);
 };
 
 struct CodeOriginAtCallReturnOffset {
diff --git a/Source/JavaScriptCore/bytecode/DFGExitProfile.cpp b/Source/JavaScriptCore/bytecode/DFGExitProfile.cpp
index 69fdc37..9f7e901 100644
--- a/Source/JavaScriptCore/bytecode/DFGExitProfile.cpp
+++ b/Source/JavaScriptCore/bytecode/DFGExitProfile.cpp
@@ -55,6 +55,21 @@
     return true;
 }
 
+Vector<FrequentExitSite> ExitProfile::exitSitesFor(unsigned bytecodeIndex)
+{
+    Vector<FrequentExitSite> result;
+    
+    if (!m_frequentExitSites)
+        return result;
+    
+    for (unsigned i = 0; i < m_frequentExitSites->size(); ++i) {
+        if (m_frequentExitSites->at(i).bytecodeOffset() == bytecodeIndex)
+            result.append(m_frequentExitSites->at(i));
+    }
+    
+    return result;
+}
+
 QueryableExitProfile::QueryableExitProfile(const ExitProfile& profile)
 {
     if (!profile.m_frequentExitSites)
diff --git a/Source/JavaScriptCore/bytecode/DFGExitProfile.h b/Source/JavaScriptCore/bytecode/DFGExitProfile.h
index 74dabae..61466f4 100644
--- a/Source/JavaScriptCore/bytecode/DFGExitProfile.h
+++ b/Source/JavaScriptCore/bytecode/DFGExitProfile.h
@@ -129,6 +129,10 @@
     // anyway.
     bool add(const FrequentExitSite&);
     
+    // Get the frequent exit sites for a bytecode index. This is O(n), and is
+    // meant to only be used from debugging/profiling code.
+    Vector<FrequentExitSite> exitSitesFor(unsigned bytecodeIndex);
+    
 private:
     friend class QueryableExitProfile;
     
diff --git a/Source/JavaScriptCore/jit/JITStubs.cpp b/Source/JavaScriptCore/jit/JITStubs.cpp
index 3490704..d2ca4c5 100644
--- a/Source/JavaScriptCore/jit/JITStubs.cpp
+++ b/Source/JavaScriptCore/jit/JITStubs.cpp
@@ -2222,6 +2222,7 @@
     if (shouldLink) {
         ASSERT(codePtr);
         JIT::compileClosureCall(globalData, callLinkInfo, callerCodeBlock, calleeCodeBlock, structure, executable, codePtr);
+        callLinkInfo->hasSeenClosure = true;
     } else
         JIT::linkSlowCall(callerCodeBlock, callLinkInfo);