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);