JavaScriptCore does not have tiered compilation
https://bugs.webkit.org/show_bug.cgi?id=67176
Reviewed by Gavin Barraclough.
This adds the ability to have multiple CodeBlocks associated with
a particular role in an Executable. These are stored in
descending order of compiler tier. CodeBlocks are optimized when
a counter (m_executeCounter) that is incremented in loops and
epilogues becomes positive. Optimizing means that all calls to
the old CodeBlock are unlinked.
The DFG can now pull in predictions from ValueProfiles, and
propagate them along the graph. To support the new phase while
maintaing some level of abstraction, a DFGDriver was introduced
that encapsulates how to run the DFG compiler.
This is turned off by default because it's not yet a performance
win on all benchmarks. It speeds up crypto and richards by
10% and 6% respectively, but still does not do as good of a job
as it could. Notably, the DFG backend has not changed, and
is largely oblivious to the new information being made available
to it.
When turned off (the default), this patch is performance neutral.
* CMakeLists.txt:
* GNUmakefile.am:
* GNUmakefile.list.am:
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCoreCommon.vsprops:
* JavaScriptCore.vcproj/JavaScriptCore/copy-files.cmd:
* JavaScriptCore.xcodeproj/project.pbxproj:
* assembler/MacroAssemblerX86.h:
(JSC::MacroAssemblerX86::branchAdd32):
* assembler/MacroAssemblerX86_64.h:
(JSC::MacroAssemblerX86_64::branchAdd32):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::CodeBlock):
(JSC::CodeBlock::~CodeBlock):
(JSC::CodeBlock::visitAggregate):
(JSC::CallLinkInfo::unlink):
(JSC::CodeBlock::unlinkCalls):
(JSC::CodeBlock::unlinkIncomingCalls):
(JSC::CodeBlock::clearEvalCache):
(JSC::replaceExistingEntries):
(JSC::CodeBlock::copyDataFromAlternative):
(JSC::ProgramCodeBlock::replacement):
(JSC::EvalCodeBlock::replacement):
(JSC::FunctionCodeBlock::replacement):
(JSC::ProgramCodeBlock::compileOptimized):
(JSC::EvalCodeBlock::compileOptimized):
(JSC::FunctionCodeBlock::compileOptimized):
* bytecode/CodeBlock.h:
(JSC::GlobalCodeBlock::GlobalCodeBlock):
(JSC::ProgramCodeBlock::ProgramCodeBlock):
(JSC::EvalCodeBlock::EvalCodeBlock):
(JSC::FunctionCodeBlock::FunctionCodeBlock):
* bytecode/ValueProfile.h:
(JSC::ValueProfile::dump):
(JSC::ValueProfile::computeStatistics):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
* bytecompiler/BytecodeGenerator.h:
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::ByteCodeParser):
(JSC::DFG::ByteCodeParser::addCall):
(JSC::DFG::ByteCodeParser::dynamicallyPredict):
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::parse):
* dfg/DFGDriver.cpp: Added.
(JSC::DFG::compile):
(JSC::DFG::tryCompile):
(JSC::DFG::tryCompileFunction):
* dfg/DFGDriver.h: Added.
(JSC::DFG::tryCompile):
(JSC::DFG::tryCompileFunction):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
(JSC::DFG::Graph::predictArgumentTypes):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::predict):
(JSC::DFG::Graph::predictGlobalVar):
(JSC::DFG::Graph::isConstant):
(JSC::DFG::Graph::isJSConstant):
(JSC::DFG::Graph::isInt32Constant):
(JSC::DFG::Graph::isDoubleConstant):
(JSC::DFG::Graph::valueOfJSConstant):
(JSC::DFG::Graph::valueOfInt32Constant):
(JSC::DFG::Graph::valueOfDoubleConstant):
* dfg/DFGJITCompiler.cpp:
(JSC::DFG::JITCompiler::link):
* dfg/DFGJITCompiler.h:
(JSC::DFG::JITCompiler::isConstant):
(JSC::DFG::JITCompiler::isJSConstant):
(JSC::DFG::JITCompiler::isInt32Constant):
(JSC::DFG::JITCompiler::isDoubleConstant):
(JSC::DFG::JITCompiler::valueOfJSConstant):
(JSC::DFG::JITCompiler::valueOfInt32Constant):
(JSC::DFG::JITCompiler::valueOfDoubleConstant):
* dfg/DFGNode.h:
(JSC::DFG::isCellPrediction):
(JSC::DFG::isNumberPrediction):
(JSC::DFG::predictionToString):
(JSC::DFG::mergePrediction):
(JSC::DFG::makePrediction):
(JSC::DFG::Node::valueOfJSConstant):
(JSC::DFG::Node::isInt32Constant):
(JSC::DFG::Node::isDoubleConstant):
(JSC::DFG::Node::valueOfInt32Constant):
(JSC::DFG::Node::valueOfDoubleConstant):
(JSC::DFG::Node::predict):
* dfg/DFGPropagation.cpp: Added.
(JSC::DFG::Propagator::Propagator):
(JSC::DFG::Propagator::fixpoint):
(JSC::DFG::Propagator::setPrediction):
(JSC::DFG::Propagator::mergePrediction):
(JSC::DFG::Propagator::propagateNode):
(JSC::DFG::Propagator::propagateForward):
(JSC::DFG::Propagator::propagateBackward):
(JSC::DFG::propagate):
* dfg/DFGPropagation.h: Added.
(JSC::DFG::propagate):
* dfg/DFGRepatch.cpp:
(JSC::DFG::dfgLinkFor):
* heap/HandleHeap.h:
(JSC::HandleHeap::Node::Node):
* jit/JIT.cpp:
(JSC::JIT::emitOptimizationCheck):
(JSC::JIT::emitTimeoutCheck):
(JSC::JIT::privateCompile):
(JSC::JIT::linkFor):
* jit/JIT.h:
(JSC::JIT::emitOptimizationCheck):
* jit/JITCall32_64.cpp:
(JSC::JIT::emit_op_ret):
(JSC::JIT::emit_op_ret_object_or_this):
* jit/JITCode.h:
(JSC::JITCode::JITCode):
(JSC::JITCode::bottomTierJIT):
(JSC::JITCode::topTierJIT):
(JSC::JITCode::nextTierJIT):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_ret):
(JSC::JIT::emit_op_ret_object_or_this):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* jit/JITStubs.h:
* runtime/Executable.cpp:
(JSC::EvalExecutable::compileOptimized):
(JSC::EvalExecutable::compileInternal):
(JSC::ProgramExecutable::compileOptimized):
(JSC::ProgramExecutable::compileInternal):
(JSC::FunctionExecutable::compileOptimizedForCall):
(JSC::FunctionExecutable::compileOptimizedForConstruct):
(JSC::FunctionExecutable::compileForCallInternal):
(JSC::FunctionExecutable::compileForConstructInternal):
* runtime/Executable.h:
(JSC::EvalExecutable::compile):
(JSC::ProgramExecutable::compile):
(JSC::FunctionExecutable::compileForCall):
(JSC::FunctionExecutable::compileForConstruct):
(JSC::FunctionExecutable::compileOptimizedFor):
* wtf/Platform.h:
* wtf/SentinelLinkedList.h:
(WTF::BasicRawSentinelNode::BasicRawSentinelNode):
(WTF::BasicRawSentinelNode::setPrev):
(WTF::BasicRawSentinelNode::setNext):
(WTF::BasicRawSentinelNode::prev):
(WTF::BasicRawSentinelNode::next):
(WTF::BasicRawSentinelNode::isOnList):
(WTF::::remove):
(WTF::::SentinelLinkedList):
(WTF::::begin):
(WTF::::end):
(WTF::::push):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@94559 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index fb1f92b..caaf409 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -1401,9 +1401,10 @@
#endif
}
-CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, CodeType codeType, JSGlobalObject *globalObject, PassRefPtr<SourceProvider> sourceProvider, unsigned sourceOffset, SymbolTable* symTab, bool isConstructor)
+CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, CodeType codeType, JSGlobalObject *globalObject, PassRefPtr<SourceProvider> sourceProvider, unsigned sourceOffset, SymbolTable* symTab, bool isConstructor, PassOwnPtr<CodeBlock> alternative)
: m_globalObject(globalObject->globalData(), ownerExecutable, globalObject)
, m_heap(&m_globalObject->globalData().heap)
+ , m_executeCounter(-1000) // trigger optimization when sign bit clears
, m_numCalleeRegisters(0)
, m_numVars(0)
, m_numParameters(0)
@@ -1422,6 +1423,7 @@
, m_source(sourceProvider)
, m_sourceOffset(sourceOffset)
, m_symbolTable(symTab)
+ , m_alternative(alternative)
{
ASSERT(m_source);
@@ -1441,15 +1443,18 @@
fprintf(stderr, " arg = %u: ", i + 1);
} else
fprintf(stderr, " bc = %d: ", profile->bytecodeOffset);
- fprintf(stderr,
- "samples = %u, int32 = %u, double = %u, cell = %u, array = %u\n",
- profile->numberOfSamples(),
- profile->probabilityOfInt32(),
- profile->probabilityOfDouble(),
- profile->probabilityOfCell(),
- profile->probabilityOfArray());
+ profile->dump(stderr);
+ fprintf(stderr, "\n");
}
#endif
+
+ // We should not be garbage collected if there are incoming calls. But
+ // if this is called during heap destruction, then there may still be
+ // incoming calls, which is harmless.
+
+ // Note that our outgoing calls will be removed from other CodeBlocks'
+ // m_incomingCalls linked lists through the execution of the ~CallLinkInfo
+ // destructors.
#if ENABLE(JIT)
for (size_t size = m_structureStubInfos.size(), i = 0; i < size; ++i)
@@ -1519,6 +1524,8 @@
{
bool handleWeakReferences = false;
+ if (!!m_alternative)
+ m_alternative->visitAggregate(visitor);
visitor.append(&m_globalObject);
visitor.append(&m_ownerExecutable);
if (m_rareData) {
@@ -1789,8 +1796,30 @@
}
#if ENABLE(JIT)
+void CallLinkInfo::unlink(JSGlobalData& globalData, RepatchBuffer& repatchBuffer)
+{
+ ASSERT(isLinked());
+
+ if (isDFG) {
+#if ENABLE(DFG_JIT)
+ repatchBuffer.relink(CodeLocationCall(callReturnLocation), isCall ? operationLinkCall : operationLinkConstruct);
+#else
+ ASSERT_NOT_REACHED();
+#endif
+ } else
+ repatchBuffer.relink(CodeLocationNearCall(callReturnLocation), isCall? globalData.jitStubs->ctiVirtualCallLink() : globalData.jitStubs->ctiVirtualConstructLink());
+ hasSeenShouldRepatch = false;
+ callee.clear();
+
+ // It will be on a list if the callee has a code block.
+ if (isOnList())
+ remove();
+}
+
void CodeBlock::unlinkCalls()
{
+ if (!!m_alternative)
+ m_alternative->unlinkCalls();
if (!(m_callLinkInfos.size() || m_methodCallLinkInfos.size()))
return;
if (!m_globalData->canUseJIT())
@@ -1799,24 +1828,101 @@
for (size_t i = 0; i < m_callLinkInfos.size(); i++) {
if (!m_callLinkInfos[i].isLinked())
continue;
- if (getJITCode().jitType() == JITCode::DFGJIT) {
-#if ENABLE(DFG_JIT)
- repatchBuffer.relink(CodeLocationCall(m_callLinkInfos[i].callReturnLocation), m_callLinkInfos[i].isCall ? operationLinkCall : operationLinkConstruct);
-#else
- ASSERT_NOT_REACHED();
-#endif
- } else
- repatchBuffer.relink(CodeLocationNearCall(m_callLinkInfos[i].callReturnLocation), m_callLinkInfos[i].isCall ? m_globalData->jitStubs->ctiVirtualCallLink() : m_globalData->jitStubs->ctiVirtualConstructLink());
- m_callLinkInfos[i].unlink();
+ m_callLinkInfos[i].unlink(*m_globalData, repatchBuffer);
}
}
+
+void CodeBlock::unlinkIncomingCalls()
+{
+ RepatchBuffer repatchBuffer(this);
+ while (m_incomingCalls.begin() != m_incomingCalls.end())
+ m_incomingCalls.begin()->unlink(*m_globalData, repatchBuffer);
+}
#endif
void CodeBlock::clearEvalCache()
{
+ if (!!m_alternative)
+ m_alternative->clearEvalCache();
if (!m_rareData)
return;
m_rareData->m_evalCodeCache.clear();
}
+template<typename T>
+inline void replaceExistingEntries(Vector<T>& target, Vector<T>& source)
+{
+ ASSERT(target.size() <= source.size());
+ for (size_t i = 0; i < target.size(); ++i)
+ target[i] = source[i];
+}
+
+void CodeBlock::copyDataFromAlternative()
+{
+ if (!m_alternative)
+ return;
+
+ replaceExistingEntries(m_constantRegisters, m_alternative->m_constantRegisters);
+ replaceExistingEntries(m_functionDecls, m_alternative->m_functionDecls);
+ replaceExistingEntries(m_functionExprs, m_alternative->m_functionExprs);
+}
+
+// FIXME: Implement OSR. If compileOptimized() is called from somewhere other than the
+// epilogue, do OSR from the old code block to the new one.
+
+// FIXME: After doing successful optimized compilation, reset the profiling counter to -1, so
+// that the next execution of the old code block will jump straight into compileOptimized()
+// and perform OSR.
+
+// FIXME: Ensure that a call to compileOptimized() just does OSR (and resets the counter to -1)
+// if the code had already been compiled.
+
+CodeBlock* ProgramCodeBlock::replacement()
+{
+ return &static_cast<ProgramExecutable*>(ownerExecutable())->generatedBytecode();
+}
+
+CodeBlock* EvalCodeBlock::replacement()
+{
+ return &static_cast<EvalExecutable*>(ownerExecutable())->generatedBytecode();
+}
+
+CodeBlock* FunctionCodeBlock::replacement()
+{
+ return &static_cast<FunctionExecutable*>(ownerExecutable())->generatedBytecodeFor(m_isConstructor ? CodeForConstruct : CodeForCall);
+}
+
+JSObject* ProgramCodeBlock::compileOptimized(ExecState* exec, ScopeChainNode* scopeChainNode)
+{
+ if (replacement()->getJITType() == JITCode::nextTierJIT(getJITType())) {
+ // No OSR yet, so make sure we don't hit this again anytime soon.
+ dontOptimizeAnytimeSoon();
+ return 0;
+ }
+ JSObject* error = static_cast<ProgramExecutable*>(ownerExecutable())->compileOptimized(exec, scopeChainNode);
+ return error;
+}
+
+JSObject* EvalCodeBlock::compileOptimized(ExecState* exec, ScopeChainNode* scopeChainNode)
+{
+ if (replacement()->getJITType() == JITCode::nextTierJIT(getJITType())) {
+ // No OSR yet, so make sure we don't hit this again anytime soon.
+ dontOptimizeAnytimeSoon();
+ return 0;
+ }
+ JSObject* error = static_cast<EvalExecutable*>(ownerExecutable())->compileOptimized(exec, scopeChainNode);
+ return error;
+}
+
+JSObject* FunctionCodeBlock::compileOptimized(ExecState* exec, ScopeChainNode* scopeChainNode)
+{
+ if (replacement()->getJITType() == JITCode::nextTierJIT(getJITType())) {
+ // No OSR yet, so make sure we don't hit this again anytime soon.
+ dontOptimizeAnytimeSoon();
+ return 0;
+ }
+ JSObject* error = static_cast<FunctionExecutable*>(ownerExecutable())->compileOptimizedFor(exec, scopeChainNode, m_isConstructor ? CodeForConstruct : CodeForCall);
+ return error;
+}
+
} // namespace JSC