JavaScript Control Flow Profiler
https://bugs.webkit.org/show_bug.cgi?id=137785
Reviewed by Filip Pizlo.
This patch introduces a mechanism for JavaScriptCore to profile
which basic blocks have executed. This mechanism will then be
used by the Web Inspector to indicate which basic blocks
have and have not executed.
The profiling works by compiling in an op_profile_control_flow
at the start of every basic block. Then, whenever this op code
executes, we know that a particular basic block has executed.
When we tier up a CodeBlock that contains an op_profile_control_flow
that corresponds to an already executed basic block, we don't
have to emit code for that particular op_profile_control_flow
because the internal data structures used to keep track of
basic block locations has already recorded that the corresponding
op_profile_control_flow has executed.
* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::CodeBlock):
* bytecode/Instruction.h:
* bytecode/UnlinkedCodeBlock.cpp:
(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::addOpProfileControlFlowBytecodeOffset):
(JSC::UnlinkedCodeBlock::opProfileControlFlowBytecodeOffsets):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitProfileControlFlow):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::ConditionalNode::emitBytecode):
(JSC::IfElseNode::emitBytecode):
(JSC::WhileNode::emitBytecode):
(JSC::ForNode::emitBytecode):
(JSC::ContinueNode::emitBytecode):
(JSC::BreakNode::emitBytecode):
(JSC::ReturnNode::emitBytecode):
(JSC::CaseClauseNode::emitBytecode):
(JSC::SwitchNode::emitBytecode):
(JSC::ThrowNode::emitBytecode):
(JSC::TryNode::emitBytecode):
(JSC::ProgramNode::emitBytecode):
(JSC::FunctionNode::emitBytecode):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::basicBlockLocation):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* inspector/agents/InspectorRuntimeAgent.cpp:
(Inspector::InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_profile_control_flow):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_profile_control_flow):
* jsc.cpp:
(GlobalObject::finishCreation):
(functionFindTypeForExpression):
(functionReturnTypeFor):
(functionDumpBasicBlockExecutionRanges):
* llint/LowLevelInterpreter.asm:
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createFunctionExpr):
(JSC::ASTBuilder::createGetterOrSetterProperty):
(JSC::ASTBuilder::createFuncDeclStatement):
(JSC::ASTBuilder::endOffset):
(JSC::ASTBuilder::setStartOffset):
* parser/NodeConstructors.h:
(JSC::Node::Node):
* parser/Nodes.h:
(JSC::CaseClauseNode::setStartOffset):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseSwitchClauses):
(JSC::Parser<LexerType>::parseSwitchDefaultClause):
(JSC::Parser<LexerType>::parseBlockStatement):
(JSC::Parser<LexerType>::parseStatement):
(JSC::Parser<LexerType>::parseFunctionDeclaration):
(JSC::Parser<LexerType>::parseIfStatement):
(JSC::Parser<LexerType>::parseExpression):
(JSC::Parser<LexerType>::parseConditionalExpression):
(JSC::Parser<LexerType>::parseProperty):
(JSC::Parser<LexerType>::parseMemberExpression):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::createFunctionExpr):
(JSC::SyntaxChecker::createFuncDeclStatement):
(JSC::SyntaxChecker::createGetterOrSetterProperty):
(JSC::SyntaxChecker::operatorStackPop):
* runtime/BasicBlockLocation.cpp: Added.
(JSC::BasicBlockLocation::BasicBlockLocation):
(JSC::BasicBlockLocation::insertGap):
(JSC::BasicBlockLocation::getExecutedRanges):
(JSC::BasicBlockLocation::dumpData):
(JSC::BasicBlockLocation::emitExecuteCode):
* runtime/BasicBlockLocation.h: Added.
(JSC::BasicBlockLocation::startOffset):
(JSC::BasicBlockLocation::endOffset):
(JSC::BasicBlockLocation::setStartOffset):
(JSC::BasicBlockLocation::setEndOffset):
(JSC::BasicBlockLocation::hasExecuted):
* runtime/CodeCache.cpp:
(JSC::CodeCache::getGlobalCodeBlock):
* runtime/ControlFlowProfiler.cpp: Added.
(JSC::ControlFlowProfiler::~ControlFlowProfiler):
(JSC::ControlFlowProfiler::getBasicBlockLocation):
(JSC::ControlFlowProfiler::dumpData):
(JSC::ControlFlowProfiler::getBasicBlocksForSourceID):
* runtime/ControlFlowProfiler.h: Added. This class is in
charge of generating BasicBlockLocations and also
providing an interface that the Web Inspector can use to ping
which basic blocks have executed based on the source id of a script.
(JSC::BasicBlockKey::BasicBlockKey):
(JSC::BasicBlockKey::isHashTableDeletedValue):
(JSC::BasicBlockKey::operator==):
(JSC::BasicBlockKey::hash):
(JSC::BasicBlockKeyHash::hash):
(JSC::BasicBlockKeyHash::equal):
* runtime/Executable.cpp:
(JSC::ProgramExecutable::ProgramExecutable):
(JSC::ProgramExecutable::initializeGlobalProperties):
* runtime/FunctionHasExecutedCache.cpp:
(JSC::FunctionHasExecutedCache::getUnexecutedFunctionRanges):
* runtime/FunctionHasExecutedCache.h:
* runtime/Options.h:
* runtime/TypeProfiler.cpp:
(JSC::TypeProfiler::logTypesForTypeLocation):
(JSC::TypeProfiler::typeInformationForExpressionAtOffset):
(JSC::TypeProfiler::findLocation):
(JSC::TypeProfiler::dumpTypeProfilerData):
* runtime/TypeProfiler.h:
(JSC::TypeProfiler::functionHasExecutedCache): Deleted.
* runtime/VM.cpp:
(JSC::VM::VM):
(JSC::enableProfilerWithRespectToCount):
(JSC::disableProfilerWithRespectToCount):
(JSC::VM::enableTypeProfiler):
(JSC::VM::disableTypeProfiler):
(JSC::VM::enableControlFlowProfiler):
(JSC::VM::disableControlFlowProfiler):
(JSC::VM::dumpTypeProfilerData):
* runtime/VM.h:
(JSC::VM::functionHasExecutedCache):
(JSC::VM::controlFlowProfiler):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@176836 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index fbd63f0..c236ffe 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -407,6 +407,7 @@
runtime/ArrayIteratorConstructor.cpp
runtime/ArrayIteratorPrototype.cpp
runtime/ArrayPrototype.cpp
+ runtime/BasicBlockLocation.cpp
runtime/BooleanConstructor.cpp
runtime/BooleanObject.cpp
runtime/BooleanPrototype.cpp
@@ -422,6 +423,7 @@
runtime/ConsoleClient.cpp
runtime/ConsolePrototype.cpp
runtime/ConstructData.cpp
+ runtime/ControlFlowProfiler.cpp
runtime/CustomGetterSetter.cpp
runtime/DataView.cpp
runtime/DateConstructor.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index c7f8a9a..21c97c8 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,181 @@
+2014-12-04 Saam Barati <saambarati1@gmail.com>
+
+ JavaScript Control Flow Profiler
+ https://bugs.webkit.org/show_bug.cgi?id=137785
+
+ Reviewed by Filip Pizlo.
+
+ This patch introduces a mechanism for JavaScriptCore to profile
+ which basic blocks have executed. This mechanism will then be
+ used by the Web Inspector to indicate which basic blocks
+ have and have not executed.
+
+ The profiling works by compiling in an op_profile_control_flow
+ at the start of every basic block. Then, whenever this op code
+ executes, we know that a particular basic block has executed.
+
+ When we tier up a CodeBlock that contains an op_profile_control_flow
+ that corresponds to an already executed basic block, we don't
+ have to emit code for that particular op_profile_control_flow
+ because the internal data structures used to keep track of
+ basic block locations has already recorded that the corresponding
+ op_profile_control_flow has executed.
+
+ * CMakeLists.txt:
+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * bytecode/BytecodeList.json:
+ * bytecode/BytecodeUseDef.h:
+ (JSC::computeUsesForBytecodeOffset):
+ (JSC::computeDefsForBytecodeOffset):
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dumpBytecode):
+ (JSC::CodeBlock::CodeBlock):
+ * bytecode/Instruction.h:
+ * bytecode/UnlinkedCodeBlock.cpp:
+ (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
+ * bytecode/UnlinkedCodeBlock.h:
+ (JSC::UnlinkedCodeBlock::addOpProfileControlFlowBytecodeOffset):
+ (JSC::UnlinkedCodeBlock::opProfileControlFlowBytecodeOffsets):
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::emitProfileControlFlow):
+ * bytecompiler/BytecodeGenerator.h:
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::ConditionalNode::emitBytecode):
+ (JSC::IfElseNode::emitBytecode):
+ (JSC::WhileNode::emitBytecode):
+ (JSC::ForNode::emitBytecode):
+ (JSC::ContinueNode::emitBytecode):
+ (JSC::BreakNode::emitBytecode):
+ (JSC::ReturnNode::emitBytecode):
+ (JSC::CaseClauseNode::emitBytecode):
+ (JSC::SwitchNode::emitBytecode):
+ (JSC::ThrowNode::emitBytecode):
+ (JSC::TryNode::emitBytecode):
+ (JSC::ProgramNode::emitBytecode):
+ (JSC::FunctionNode::emitBytecode):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGCapabilities.cpp:
+ (JSC::DFG::capabilityLevel):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::basicBlockLocation):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * inspector/agents/InspectorRuntimeAgent.cpp:
+ (Inspector::InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets):
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileMainPass):
+ * jit/JIT.h:
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_profile_control_flow):
+ * jit/JITOpcodes32_64.cpp:
+ (JSC::JIT::emit_op_profile_control_flow):
+ * jsc.cpp:
+ (GlobalObject::finishCreation):
+ (functionFindTypeForExpression):
+ (functionReturnTypeFor):
+ (functionDumpBasicBlockExecutionRanges):
+ * llint/LowLevelInterpreter.asm:
+ * parser/ASTBuilder.h:
+ (JSC::ASTBuilder::createFunctionExpr):
+ (JSC::ASTBuilder::createGetterOrSetterProperty):
+ (JSC::ASTBuilder::createFuncDeclStatement):
+ (JSC::ASTBuilder::endOffset):
+ (JSC::ASTBuilder::setStartOffset):
+ * parser/NodeConstructors.h:
+ (JSC::Node::Node):
+ * parser/Nodes.h:
+ (JSC::CaseClauseNode::setStartOffset):
+ * parser/Parser.cpp:
+ (JSC::Parser<LexerType>::parseSwitchClauses):
+ (JSC::Parser<LexerType>::parseSwitchDefaultClause):
+ (JSC::Parser<LexerType>::parseBlockStatement):
+ (JSC::Parser<LexerType>::parseStatement):
+ (JSC::Parser<LexerType>::parseFunctionDeclaration):
+ (JSC::Parser<LexerType>::parseIfStatement):
+ (JSC::Parser<LexerType>::parseExpression):
+ (JSC::Parser<LexerType>::parseConditionalExpression):
+ (JSC::Parser<LexerType>::parseProperty):
+ (JSC::Parser<LexerType>::parseMemberExpression):
+ * parser/SyntaxChecker.h:
+ (JSC::SyntaxChecker::createFunctionExpr):
+ (JSC::SyntaxChecker::createFuncDeclStatement):
+ (JSC::SyntaxChecker::createGetterOrSetterProperty):
+ (JSC::SyntaxChecker::operatorStackPop):
+ * runtime/BasicBlockLocation.cpp: Added.
+ (JSC::BasicBlockLocation::BasicBlockLocation):
+ (JSC::BasicBlockLocation::insertGap):
+ (JSC::BasicBlockLocation::getExecutedRanges):
+ (JSC::BasicBlockLocation::dumpData):
+ (JSC::BasicBlockLocation::emitExecuteCode):
+ * runtime/BasicBlockLocation.h: Added.
+ (JSC::BasicBlockLocation::startOffset):
+ (JSC::BasicBlockLocation::endOffset):
+ (JSC::BasicBlockLocation::setStartOffset):
+ (JSC::BasicBlockLocation::setEndOffset):
+ (JSC::BasicBlockLocation::hasExecuted):
+ * runtime/CodeCache.cpp:
+ (JSC::CodeCache::getGlobalCodeBlock):
+ * runtime/ControlFlowProfiler.cpp: Added.
+ (JSC::ControlFlowProfiler::~ControlFlowProfiler):
+ (JSC::ControlFlowProfiler::getBasicBlockLocation):
+ (JSC::ControlFlowProfiler::dumpData):
+ (JSC::ControlFlowProfiler::getBasicBlocksForSourceID):
+ * runtime/ControlFlowProfiler.h: Added. This class is in
+ charge of generating BasicBlockLocations and also
+ providing an interface that the Web Inspector can use to ping
+ which basic blocks have executed based on the source id of a script.
+
+ (JSC::BasicBlockKey::BasicBlockKey):
+ (JSC::BasicBlockKey::isHashTableDeletedValue):
+ (JSC::BasicBlockKey::operator==):
+ (JSC::BasicBlockKey::hash):
+ (JSC::BasicBlockKeyHash::hash):
+ (JSC::BasicBlockKeyHash::equal):
+ * runtime/Executable.cpp:
+ (JSC::ProgramExecutable::ProgramExecutable):
+ (JSC::ProgramExecutable::initializeGlobalProperties):
+ * runtime/FunctionHasExecutedCache.cpp:
+ (JSC::FunctionHasExecutedCache::getUnexecutedFunctionRanges):
+ * runtime/FunctionHasExecutedCache.h:
+ * runtime/Options.h:
+ * runtime/TypeProfiler.cpp:
+ (JSC::TypeProfiler::logTypesForTypeLocation):
+ (JSC::TypeProfiler::typeInformationForExpressionAtOffset):
+ (JSC::TypeProfiler::findLocation):
+ (JSC::TypeProfiler::dumpTypeProfilerData):
+ * runtime/TypeProfiler.h:
+ (JSC::TypeProfiler::functionHasExecutedCache): Deleted.
+ * runtime/VM.cpp:
+ (JSC::VM::VM):
+ (JSC::enableProfilerWithRespectToCount):
+ (JSC::disableProfilerWithRespectToCount):
+ (JSC::VM::enableTypeProfiler):
+ (JSC::VM::disableTypeProfiler):
+ (JSC::VM::enableControlFlowProfiler):
+ (JSC::VM::disableControlFlowProfiler):
+ (JSC::VM::dumpTypeProfilerData):
+ * runtime/VM.h:
+ (JSC::VM::functionHasExecutedCache):
+ (JSC::VM::controlFlowProfiler):
+
2014-12-04 Filip Pizlo <fpizlo@apple.com>
printInternal(PrintStream& out, JSC::JITCode::JITType type) ends up dumping a literal %s
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
index f2948a3..267b56e 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
@@ -674,6 +674,7 @@
<ClCompile Include="..\runtime\ArrayIteratorConstructor.cpp" />
<ClCompile Include="..\runtime\ArrayIteratorPrototype.cpp" />
<ClCompile Include="..\runtime\ArrayPrototype.cpp" />
+ <ClCompile Include="..\runtime\BasicBlockLocation.cpp" />
<ClCompile Include="..\runtime\BooleanConstructor.cpp" />
<ClCompile Include="..\runtime\BooleanObject.cpp" />
<ClCompile Include="..\runtime\BooleanPrototype.cpp" />
@@ -688,6 +689,7 @@
<ClCompile Include="..\runtime\ConsoleClient.cpp" />
<ClCompile Include="..\runtime\ConsolePrototype.cpp" />
<ClCompile Include="..\runtime\ConstructData.cpp" />
+ <ClCompile Include="..\runtime\ControlFlowProfiler.cpp" />
<ClCompile Include="..\runtime\CustomGetterSetter.cpp" />
<ClCompile Include="..\runtime\DataView.cpp" />
<ClCompile Include="..\runtime\DateConstructor.cpp" />
@@ -1422,6 +1424,7 @@
<ClInclude Include="..\runtime\ArrayIteratorPrototype.h" />
<ClInclude Include="..\runtime\ArrayPrototype.h" />
<ClInclude Include="..\runtime\ArrayStorage.h" />
+ <ClInclude Include="..\runtime\BasicBlockLocation.h" />
<ClInclude Include="..\runtime\BatchedTransitionOptimizer.h" />
<ClInclude Include="..\runtime\BigInteger.h" />
<ClInclude Include="..\runtime\BooleanConstructor.h" />
@@ -1443,6 +1446,7 @@
<ClInclude Include="..\runtime\ConsoleTypes.h" />
<ClInclude Include="..\runtime\ConstantMode.h" />
<ClInclude Include="..\runtime\ConstructData.h" />
+ <ClInclude Include="..\runtime\ControlFlowProfiler.h" />
<ClInclude Include="..\runtime\CustomGetterSetter.h" />
<ClInclude Include="..\runtime\DataView.h" />
<ClInclude Include="..\runtime\DateConstructor.h" />
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
index 0544756..dcddf25 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
@@ -534,6 +534,9 @@
<ClCompile Include="..\runtime\ArrayPrototype.cpp">
<Filter>runtime</Filter>
</ClCompile>
+ <ClCompile Include="..\runtime\BasicBlockLocation.cpp">
+ <Filter>runtime</Filter>
+ </ClCompile>
<ClCompile Include="..\runtime\BooleanConstructor.cpp">
<Filter>runtime</Filter>
</ClCompile>
@@ -567,6 +570,9 @@
<ClCompile Include="..\runtime\ConstructData.cpp">
<Filter>runtime</Filter>
</ClCompile>
+ <ClCompile Include="..\runtime\ControlFlowProfiler.cpp">
+ <Filter>runtime</Filter>
+ </ClCompile>
<ClCompile Include="..\runtime\CustomGetterSetter.cpp">
<Filter>runtime</Filter>
</ClCompile>
@@ -2510,6 +2516,9 @@
<ClInclude Include="..\runtime\ArrayStorage.h">
<Filter>runtime</Filter>
</ClInclude>
+ <ClInclude Include="..\runtime\BasicBlockLocation.h">
+ <Filter>runtime</Filter>
+ </ClInclude>
<ClInclude Include="..\runtime\BatchedTransitionOptimizer.h">
<Filter>runtime</Filter>
</ClInclude>
@@ -2564,6 +2573,9 @@
<ClInclude Include="..\runtime\ConstructData.h">
<Filter>runtime</Filter>
</ClInclude>
+ <ClInclude Include="..\runtime\ControlFlowProfiler.h">
+ <Filter>runtime</Filter>
+ </ClInclude>
<ClInclude Include="..\runtime\CustomGetterSetter.h">
<Filter>runtime</Filter>
</ClInclude>
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index fe8441b..0f820d2 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -896,10 +896,14 @@
41359CF30FDD89AD00206180 /* DateConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = D21202290AD4310C00ED79B6 /* DateConversion.h */; };
4443AE3316E188D90076F110 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51F0EB6105C86C6B00E6DF1B /* Foundation.framework */; };
451539B912DC994500EF7AC4 /* Yarr.h in Headers */ = {isa = PBXBuildFile; fileRef = 451539B812DC994500EF7AC4 /* Yarr.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 52678F8E1A031009006A306D /* BasicBlockLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52678F8C1A031009006A306D /* BasicBlockLocation.cpp */; };
+ 52678F8F1A031009006A306D /* BasicBlockLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 52678F8D1A031009006A306D /* BasicBlockLocation.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 52678F911A04177C006A306D /* ControlFlowProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 52678F901A04177C006A306D /* ControlFlowProfiler.h */; settings = {ATTRIBUTES = (Private, ); }; };
52B310FB1974AE610080857C /* FunctionHasExecutedCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B310FA1974AE610080857C /* FunctionHasExecutedCache.h */; settings = {ATTRIBUTES = (Private, ); }; };
52B310FD1974AE870080857C /* FunctionHasExecutedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52B310FC1974AE870080857C /* FunctionHasExecutedCache.cpp */; };
52B310FF1975B4240080857C /* TypeLocationCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52B310FE1975B4240080857C /* TypeLocationCache.cpp */; };
52B311011975B4670080857C /* TypeLocationCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B311001975B4670080857C /* TypeLocationCache.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 52B717B51A0597E1007AF4F3 /* ControlFlowProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52B717B41A0597E1007AF4F3 /* ControlFlowProfiler.cpp */; };
52C952B719A289850069B386 /* TypeProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 52C952B619A289850069B386 /* TypeProfiler.h */; settings = {ATTRIBUTES = (Private, ); }; };
52C952B919A28A1C0069B386 /* TypeProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52C952B819A28A1C0069B386 /* TypeProfiler.cpp */; };
5D53726F0E1C54880021E549 /* Tracing.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D53726E0E1C54880021E549 /* Tracing.h */; };
@@ -2528,10 +2532,14 @@
45E12D8806A49B0F00E9DF84 /* jsc.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc.cpp; sourceTree = "<group>"; tabWidth = 4; };
51F0EB6105C86C6B00E6DF1B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
51F0EC0705C86C9A00E6DF1B /* libobjc.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libobjc.dylib; path = /usr/lib/libobjc.dylib; sourceTree = "<absolute>"; };
+ 52678F8C1A031009006A306D /* BasicBlockLocation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BasicBlockLocation.cpp; sourceTree = "<group>"; };
+ 52678F8D1A031009006A306D /* BasicBlockLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BasicBlockLocation.h; sourceTree = "<group>"; };
+ 52678F901A04177C006A306D /* ControlFlowProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ControlFlowProfiler.h; sourceTree = "<group>"; };
52B310FA1974AE610080857C /* FunctionHasExecutedCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FunctionHasExecutedCache.h; sourceTree = "<group>"; };
52B310FC1974AE870080857C /* FunctionHasExecutedCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FunctionHasExecutedCache.cpp; sourceTree = "<group>"; };
52B310FE1975B4240080857C /* TypeLocationCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TypeLocationCache.cpp; sourceTree = "<group>"; };
52B311001975B4670080857C /* TypeLocationCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TypeLocationCache.h; sourceTree = "<group>"; };
+ 52B717B41A0597E1007AF4F3 /* ControlFlowProfiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ControlFlowProfiler.cpp; sourceTree = "<group>"; };
52C952B619A289850069B386 /* TypeProfiler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TypeProfiler.h; sourceTree = "<group>"; };
52C952B819A28A1C0069B386 /* TypeProfiler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TypeProfiler.cpp; sourceTree = "<group>"; };
5540758418F4A37500602A5D /* CompileRuntimeToLLVMIR.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = CompileRuntimeToLLVMIR.xcconfig; sourceTree = "<group>"; };
@@ -4176,6 +4184,10 @@
F692A84D0255597D01FF60F7 /* ArrayPrototype.cpp */,
F692A84E0255597D01FF60F7 /* ArrayPrototype.h */,
0FB7F38A15ED8E3800F167B2 /* ArrayStorage.h */,
+ 52678F8C1A031009006A306D /* BasicBlockLocation.cpp */,
+ 52678F8D1A031009006A306D /* BasicBlockLocation.h */,
+ 52B717B41A0597E1007AF4F3 /* ControlFlowProfiler.cpp */,
+ 52678F901A04177C006A306D /* ControlFlowProfiler.h */,
147B83AA0E6DB8C9004775A4 /* BatchedTransitionOptimizer.h */,
866739D013BFDE710023D87C /* BigInteger.h */,
BC7952320E15EB5600A898AB /* BooleanConstructor.cpp */,
@@ -5367,6 +5379,8 @@
0F6B1CB91861244C00845D97 /* ArityCheckMode.h in Headers */,
A1A009C11831A26E00CF8711 /* ARM64Assembler.h in Headers */,
86D3B2C410156BDE002865E7 /* ARMAssembler.h in Headers */,
+ 52678F911A04177C006A306D /* ControlFlowProfiler.h in Headers */,
+ 52678F8F1A031009006A306D /* BasicBlockLocation.h in Headers */,
A5EA710E19F6DF810098F5EC /* InspectorAlternateBackendDispatchers.h in Headers */,
A5EA70EC19F5B3EA0098F5EC /* generate_cpp_alternate_backend_dispatcher_header.py in Headers */,
C4F4B6F31A05C944005CAB76 /* cpp_generator_templates.py in Headers */,
@@ -6879,6 +6893,7 @@
A7D89CFF17A0B8CC00773AD8 /* DFGSSAConversionPhase.cpp in Sources */,
2A05ABD51961DF2400341750 /* JSPropertyNameEnumerator.cpp in Sources */,
0FC20CB918556A3500C9E954 /* DFGSSALoweringPhase.cpp in Sources */,
+ 52B717B51A0597E1007AF4F3 /* ControlFlowProfiler.cpp in Sources */,
0FC3CD0119ADA411006AC72A /* DFGNaiveDominators.cpp in Sources */,
0F9FB4F417FCB91700CB67F8 /* DFGStackLayoutPhase.cpp in Sources */,
0F4F29DF18B6AD1C0057BC15 /* DFGStaticExecutionCountEstimationPhase.cpp in Sources */,
@@ -6919,6 +6934,7 @@
A7B48F490EE8936F00DCBDB6 /* ExecutableAllocator.cpp in Sources */,
86DB64640F95C6FC00D7D921 /* ExecutableAllocatorFixedVMPool.cpp in Sources */,
0F56A1D515001CF4002992B1 /* ExecutionCounter.cpp in Sources */,
+ 52678F8E1A031009006A306D /* BasicBlockLocation.cpp in Sources */,
0F2D4DEB19832DC4007D4B19 /* TypeProfilerLog.cpp in Sources */,
0F0332C018ADFAE1005F979A /* ExitingJITType.cpp in Sources */,
0FB105851675480F00F8AB6E /* ExitKind.cpp in Sources */,
diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.json b/Source/JavaScriptCore/bytecode/BytecodeList.json
index e694218..1e3b5b3 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeList.json
+++ b/Source/JavaScriptCore/bytecode/BytecodeList.json
@@ -119,6 +119,7 @@
{ "name" : "op_profile_did_call", "length" : 2 },
{ "name" : "op_end", "length" : 2 },
{ "name" : "op_profile_type", "length" : 6 },
+ { "name" : "op_profile_control_flow", "length" : 2 },
{ "name" : "op_get_enumerable_length", "length" : 3 },
{ "name" : "op_has_indexed_property", "length" : 5 },
{ "name" : "op_has_structure_property", "length" : 5 },
diff --git a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
index cd0848c..1420169 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
+++ b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
@@ -53,6 +53,7 @@
case op_enter:
case op_catch:
case op_touch_entry:
+ case op_profile_control_flow:
return;
case op_create_lexical_environment:
case op_get_scope:
@@ -283,6 +284,7 @@
case op_put_by_index:
case op_tear_off_arguments:
case op_profile_type:
+ case op_profile_control_flow:
case op_touch_entry:
#define LLINT_HELPER_OPCODES(opcode, length) case opcode:
FOR_EACH_LLINT_OPCODE_EXTENSION(LLINT_HELPER_OPCODES);
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index 55693c8..de2bf66 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -30,6 +30,7 @@
#include "config.h"
#include "CodeBlock.h"
+#include "BasicBlockLocation.h"
#include "BytecodeGenerator.h"
#include "BytecodeUseDef.h"
#include "CallLinkStatus.h"
@@ -850,6 +851,12 @@
out.printf("%s", registerName(r0).data());
break;
}
+ case op_profile_control_flow: {
+ BasicBlockLocation* basicBlockLocation = (++it)->u.basicBlockLocation;
+ printLocationAndOp(out, exec, location, it, "profile_control_flow");
+ out.printf("[%d, %d]", basicBlockLocation->startOffset(), basicBlockLocation->endOffset());
+ break;
+ }
case op_not: {
printUnaryOp(out, exec, location, it, "not");
break;
@@ -1728,8 +1735,8 @@
ASSERT(m_source);
setNumParameters(unlinkedCodeBlock->numParameters());
- if (vm()->typeProfiler())
- vm()->typeProfiler()->functionHasExecutedCache()->removeUnexecutedRange(m_ownerExecutable->sourceID(), m_ownerExecutable->typeProfilingStartOffset(), m_ownerExecutable->typeProfilingEndOffset());
+ if (vm()->typeProfiler() || vm()->controlFlowProfiler())
+ vm()->functionHasExecutedCache()->removeUnexecutedRange(m_ownerExecutable->sourceID(), m_ownerExecutable->typeProfilingStartOffset(), m_ownerExecutable->typeProfilingEndOffset());
setConstantRegisters(unlinkedCodeBlock->constantRegisters());
if (unlinkedCodeBlock->usesGlobalObject())
@@ -1737,8 +1744,8 @@
m_functionDecls.resizeToFit(unlinkedCodeBlock->numberOfFunctionDecls());
for (size_t count = unlinkedCodeBlock->numberOfFunctionDecls(), i = 0; i < count; ++i) {
UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionDecl(i);
- if (vm()->typeProfiler())
- vm()->typeProfiler()->functionHasExecutedCache()->insertUnexecutedRange(m_ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset());
+ if (vm()->typeProfiler() || vm()->controlFlowProfiler())
+ vm()->functionHasExecutedCache()->insertUnexecutedRange(m_ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset());
unsigned lineCount = unlinkedExecutable->lineCount();
unsigned firstLine = ownerExecutable->lineNo() + unlinkedExecutable->firstLineOffset();
bool startColumnIsOnOwnerStartLine = !unlinkedExecutable->firstLineOffset();
@@ -1755,8 +1762,8 @@
m_functionExprs.resizeToFit(unlinkedCodeBlock->numberOfFunctionExprs());
for (size_t count = unlinkedCodeBlock->numberOfFunctionExprs(), i = 0; i < count; ++i) {
UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionExpr(i);
- if (vm()->typeProfiler())
- vm()->typeProfiler()->functionHasExecutedCache()->insertUnexecutedRange(m_ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset());
+ if (vm()->typeProfiler() || vm()->controlFlowProfiler())
+ vm()->functionHasExecutedCache()->insertUnexecutedRange(m_ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset());
unsigned lineCount = unlinkedExecutable->lineCount();
unsigned firstLine = ownerExecutable->lineNo() + unlinkedExecutable->firstLineOffset();
bool startColumnIsOnOwnerStartLine = !unlinkedExecutable->firstLineOffset();
@@ -2125,6 +2132,47 @@
}
i += opLength;
}
+
+ if (vm()->controlFlowProfiler()) {
+ const Vector<size_t>& bytecodeOffsets = unlinkedCodeBlock->opProfileControlFlowBytecodeOffsets();
+ for (size_t i = 0, offsetsLength = bytecodeOffsets.size(); i < offsetsLength; i++) {
+ // Because op_profile_control_flow is emitted at the beginning of every basic block, finding
+ // the next op_profile_control_flow will give us the text range of a single basic block.
+ size_t startIdx = bytecodeOffsets[i];
+ RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[startIdx].u.opcode) == op_profile_control_flow);
+ int basicBlockStartOffset = instructions[startIdx + 1].u.operand;
+ int basicBlockEndOffset;
+ if (i + 1 < offsetsLength) {
+ size_t endIdx = bytecodeOffsets[i + 1];
+ RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[endIdx].u.opcode) == op_profile_control_flow);
+ basicBlockEndOffset = instructions[endIdx + 1].u.operand;
+ } else
+ basicBlockEndOffset = m_sourceOffset + m_ownerExecutable->source().length() - 1;
+
+ BasicBlockLocation* basicBlockLocation = vm()->controlFlowProfiler()->getBasicBlockLocation(m_ownerExecutable->sourceID(), basicBlockStartOffset, basicBlockEndOffset);
+
+ // Find all functions that are enclosed within the range: [basicBlockStartOffset, basicBlockEndOffset]
+ // and insert these functions' start/end offsets as gaps in the current BasicBlockLocation.
+ // This is necessary because in the original source text of a JavaScript program,
+ // function literals form new basic blocks boundaries, but they aren't represented
+ // inside the CodeBlock's instruction stream.
+ auto insertFunctionGaps = [basicBlockLocation, basicBlockStartOffset, basicBlockEndOffset] (const WriteBarrier<FunctionExecutable>& functionExecutable) {
+ const UnlinkedFunctionExecutable* executable = functionExecutable->unlinkedExecutable();
+ int functionStart = executable->typeProfilingStartOffset();
+ int functionEnd = executable->typeProfilingEndOffset();
+ if (functionStart >= basicBlockStartOffset && functionEnd <= basicBlockEndOffset)
+ basicBlockLocation->insertGap(functionStart, functionEnd);
+ };
+
+ for (const WriteBarrier<FunctionExecutable>& executable : m_functionDecls)
+ insertFunctionGaps(executable);
+ for (const WriteBarrier<FunctionExecutable>& executable : m_functionExprs)
+ insertFunctionGaps(executable);
+
+ instructions[startIdx + 1].u.basicBlockLocation = basicBlockLocation;
+ }
+ }
+
m_instructions = WTF::RefCountedArray<Instruction>(instructions);
// Set optimization thresholds only after m_instructions is initialized, since these
diff --git a/Source/JavaScriptCore/bytecode/Instruction.h b/Source/JavaScriptCore/bytecode/Instruction.h
index ab9d2d6..dc5aa43 100644
--- a/Source/JavaScriptCore/bytecode/Instruction.h
+++ b/Source/JavaScriptCore/bytecode/Instruction.h
@@ -29,6 +29,7 @@
#ifndef Instruction_h
#define Instruction_h
+#include "BasicBlockLocation.h"
#include "MacroAssembler.h"
#include "Opcode.h"
#include "TypeLocation.h"
@@ -122,6 +123,7 @@
bool* predicatePointer;
ToThisStatus toThisStatus;
TypeLocation* location;
+ BasicBlockLocation* basicBlockLocation;
} u;
private:
diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
index e2f1710..1aee8eb 100644
--- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
@@ -94,7 +94,7 @@
, m_unlinkedBodyEndColumn(m_lineCount ? node->endColumn() : node->endColumn() - node->startColumn())
, m_startOffset(node->source().startOffset() - source.startOffset())
, m_sourceLength(node->source().length())
- , m_typeProfilingStartOffset(node->functionNameStart())
+ , m_typeProfilingStartOffset(node->functionKeywordStart())
, m_typeProfilingEndOffset(node->startStartOffset() + node->source().length() - 1)
, m_features(0)
, m_functionMode(node->functionMode())
diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
index c74c892..f11700f 100644
--- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
+++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
@@ -487,6 +487,9 @@
ALWAYS_INLINE unsigned startColumn() const { return 0; }
unsigned endColumn() const { return m_endColumn; }
+ void addOpProfileControlFlowBytecodeOffset(size_t offset) { m_opProfileControlFlowBytecodeOffsets.append(offset); }
+ const Vector<size_t>& opProfileControlFlowBytecodeOffsets() const { return m_opProfileControlFlowBytecodeOffsets; }
+
void dumpExpressionRangeInfo(); // For debugging purpose only.
protected:
@@ -587,6 +590,7 @@
unsigned m_endDivot;
};
HashMap<unsigned, TypeProfilerExpressionRange> m_typeProfilerInfoMap;
+ Vector<size_t> m_opProfileControlFlowBytecodeOffsets;
protected:
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index c1caa21..0b15658 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -1178,6 +1178,18 @@
instructions().append(resolveType());
}
+void BytecodeGenerator::emitProfileControlFlow(int textOffset)
+{
+ if (vm()->controlFlowProfiler()) {
+ RELEASE_ASSERT(textOffset >= 0);
+ size_t bytecodeOffset = instructions().size();
+ m_codeBlock->addOpProfileControlFlowBytecodeOffset(bytecodeOffset);
+
+ emitOpcode(op_profile_control_flow);
+ instructions().append(textOffset);
+ }
+}
+
RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, bool b)
{
return emitLoad(dst, jsBoolean(b));
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
index 1d3c63d..191b064 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
@@ -439,6 +439,8 @@
void emitTypeProfilerExpressionInfo(const JSTextPosition& startDivot, const JSTextPosition& endDivot);
void emitProfileType(RegisterID* registerToProfile, ProfileTypeBytecodeFlag, const Identifier*);
+ void emitProfileControlFlow(int);
+
RegisterID* emitLoad(RegisterID* dst, bool);
RegisterID* emitLoad(RegisterID* dst, double);
RegisterID* emitLoad(RegisterID* dst, const Identifier&);
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
index 5af0454..4c66b82 100644
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
@@ -1442,14 +1442,18 @@
generator.emitNodeInConditionContext(m_logical, beforeThen.get(), beforeElse.get(), FallThroughMeansTrue);
generator.emitLabel(beforeThen.get());
+ generator.emitProfileControlFlow(m_expr1->startOffset());
generator.emitNode(newDst.get(), m_expr1);
generator.emitJump(afterElse.get());
generator.emitLabel(beforeElse.get());
+ generator.emitProfileControlFlow(m_expr2->startOffset());
generator.emitNode(newDst.get(), m_expr2);
generator.emitLabel(afterElse.get());
+ generator.emitProfileControlFlow(m_expr2->endOffset());
+
return newDst.get();
}
@@ -1887,6 +1891,7 @@
generator.emitNodeInConditionContext(m_condition, trueTarget, falseTarget, fallThroughMode);
generator.emitLabel(beforeThen.get());
+ generator.emitProfileControlFlow(m_ifBlock->startOffset());
if (!didFoldIfBlock) {
generator.emitNode(dst, m_ifBlock);
@@ -1896,10 +1901,13 @@
generator.emitLabel(beforeElse.get());
- if (m_elseBlock)
+ if (m_elseBlock) {
+ generator.emitProfileControlFlow(m_ifBlock->endOffset());
generator.emitNode(dst, m_elseBlock);
+ }
generator.emitLabel(afterElse.get());
+ generator.emitProfileControlFlow(m_elseBlock ? m_elseBlock->endOffset() : m_ifBlock->endOffset());
}
// ------------------------------ DoWhileNode ----------------------------------
@@ -1935,6 +1943,7 @@
generator.emitLabel(topOfLoop.get());
generator.emitLoopHint();
+ generator.emitProfileControlFlow(m_statement->startOffset());
generator.emitNode(dst, m_statement);
generator.emitLabel(scope->continueTarget());
@@ -1943,6 +1952,8 @@
generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), FallThroughMeansFalse);
generator.emitLabel(scope->breakTarget());
+
+ generator.emitProfileControlFlow(endOffset());
}
// ------------------------------ ForNode --------------------------------------
@@ -1962,6 +1973,7 @@
generator.emitLabel(topOfLoop.get());
generator.emitLoopHint();
+ generator.emitProfileControlFlow(m_statement->startOffset());
generator.emitNode(dst, m_statement);
@@ -1976,6 +1988,7 @@
generator.emitJump(topOfLoop.get());
generator.emitLabel(scope->breakTarget());
+ generator.emitProfileControlFlow(endOffset());
}
// ------------------------------ ForInNode ------------------------------------
@@ -2266,6 +2279,8 @@
generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth());
generator.emitJump(scope->continueTarget());
+
+ generator.emitProfileControlFlow(endOffset());
}
// ------------------------------ BreakNode ------------------------------------
@@ -2293,6 +2308,8 @@
generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth());
generator.emitJump(scope->breakTarget());
+
+ generator.emitProfileControlFlow(endOffset());
}
// ------------------------------ ReturnNode -----------------------------------
@@ -2317,6 +2334,11 @@
generator.emitDebugHook(WillLeaveCallFrame, lastLine(), startOffset(), lineStartOffset());
generator.emitReturn(returnRegister.get());
+ generator.emitProfileControlFlow(endOffset());
+ // Emitting an unreachable return here is needed in case this op_profile_control_flow is the
+ // last opcode in a CodeBlock because a CodeBlock's instructions must end with a terminal opcode.
+ if (generator.vm()->controlFlowProfiler())
+ generator.emitReturn(generator.emitLoad(nullptr, jsUndefined()));
}
// ------------------------------ WithNode -------------------------------------
@@ -2336,6 +2358,7 @@
inline void CaseClauseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
+ generator.emitProfileControlFlow(m_startOffset);
if (!m_statements)
return;
m_statements->emitBytecode(generator, dst);
@@ -2505,6 +2528,7 @@
m_block->emitBytecodeForBlock(generator, r0.get(), dst);
generator.emitLabel(scope->breakTarget());
+ generator.emitProfileControlFlow(endOffset());
}
// ------------------------------ LabelNode ------------------------------------
@@ -2532,6 +2556,8 @@
RefPtr<RegisterID> expr = generator.emitNode(m_expr);
generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
generator.emitThrow(expr.get());
+
+ generator.emitProfileControlFlow(endOffset());
}
// ------------------------------ TryNode --------------------------------------
@@ -2571,6 +2597,7 @@
}
generator.emitPushCatchScope(generator.scopeRegister(), m_exceptionIdent, exceptionRegister.get(), DontDelete);
+ generator.emitProfileControlFlow(m_tryBlock->endOffset());
generator.emitNode(dst, m_catchBlock);
generator.emitPopScope(generator.scopeRegister());
generator.emitLabel(catchEndLabel.get());
@@ -2583,6 +2610,15 @@
RefPtr<Label> finallyEndLabel = generator.newLabel();
+ // FIXME: To the JS programmer, running the normal path is the same basic block as running the uncaught exception path.
+ // But, we generate two different code paths for this, but we shouldn't generate two op_profile_control_flows for these because they
+ // logically represent the same basic block.
+ // https://bugs.webkit.org/show_bug.cgi?id=139287
+ if (m_catchBlock)
+ generator.emitProfileControlFlow(m_catchBlock->endOffset());
+ else
+ generator.emitProfileControlFlow(m_tryBlock->endOffset());
+
// Normal path: run the finally code, and jump to the end.
generator.emitNode(dst, m_finallyBlock);
generator.emitJump(finallyEndLabel.get());
@@ -2593,7 +2629,9 @@
generator.emitThrow(tempExceptionRegister.get());
generator.emitLabel(finallyEndLabel.get());
- }
+ } else
+ generator.emitProfileControlFlow(m_catchBlock->endOffset());
+
}
// ------------------------------ ScopeNode -----------------------------
@@ -2613,6 +2651,7 @@
RefPtr<RegisterID> dstRegister = generator.newTemporary();
generator.emitLoad(dstRegister.get(), jsUndefined());
+ generator.emitProfileControlFlow(startStartOffset());
emitStatementsBytecode(generator, dstRegister.get());
generator.emitDebugHook(DidExecuteProgram, lastLine(), startOffset(), lineStartOffset());
@@ -2653,6 +2692,7 @@
}
}
+ generator.emitProfileControlFlow(startStartOffset());
generator.emitDebugHook(DidEnterCallFrame, startLine(), startStartOffset(), startLineStartOffset());
emitStatementsBytecode(generator, generator.ignoredResult());
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index 73dc71b..55a4c04 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -1991,6 +1991,7 @@
case ProfileWillCall:
case ProfileDidCall:
case ProfileType:
+ case ProfileControlFlow:
case Phantom:
case HardPhantom:
case CountExecution:
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 7c075df..b91ff50 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -29,6 +29,7 @@
#if ENABLE(DFG_JIT)
#include "ArrayConstructor.h"
+#include "BasicBlockLocation.h"
#include "CallLinkStatus.h"
#include "CodeBlock.h"
#include "CodeBlockWithJITType.h"
@@ -2884,6 +2885,12 @@
NEXT_OPCODE(op_profile_type);
}
+ case op_profile_control_flow: {
+ BasicBlockLocation* basicBlockLocation = currentInstruction[1].u.basicBlockLocation;
+ addToGraph(ProfileControlFlow, OpInfo(basicBlockLocation));
+ NEXT_OPCODE(op_profile_control_flow);
+ }
+
// === Block terminators. ===
case op_jmp: {
diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
index 3d9fd3a..37b38e2 100644
--- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
@@ -121,6 +121,7 @@
case op_profile_will_call:
case op_profile_did_call:
case op_profile_type:
+ case op_profile_control_flow:
case op_mov:
case op_check_has_instance:
case op_instanceof:
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index b5d35b4..6c6927b 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -274,6 +274,7 @@
case ProfileWillCall:
case ProfileDidCall:
case ProfileType:
+ case ProfileControlFlow:
case StoreBarrier:
case StoreBarrierWithNullCheck:
case PutByOffsetHint:
diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
index 5033ba4..d635775 100644
--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
@@ -124,6 +124,7 @@
case ProfileWillCall:
case ProfileDidCall:
case ProfileType:
+ case ProfileControlFlow:
case CheckHasInstance:
case InstanceOf:
case IsUndefined:
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 18aae11..0e6e19d 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -1202,6 +1202,7 @@
case Construct:
case ProfiledCall:
case ProfiledConstruct:
+ case ProfileControlFlow:
case NativeCall:
case NativeConstruct:
case NewObject:
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index 87182aa..04eee50 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -28,6 +28,7 @@
#if ENABLE(DFG_JIT)
+#include "BasicBlockLocation.h"
#include "CodeBlock.h"
#include "DFGAbstractValue.h"
#include "DFGAdjacencyList.h"
@@ -1788,6 +1789,11 @@
{
return reinterpret_cast<TypeLocation*>(m_opInfo);
}
+
+ BasicBlockLocation* basicBlockLocation()
+ {
+ return reinterpret_cast<BasicBlockLocation*>(m_opInfo);
+ }
void dumpChildren(PrintStream& out)
{
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 9b1a4dd..8e79564 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -257,6 +257,7 @@
macro(MakeRope, NodeResultJS) \
macro(In, NodeResultBoolean | NodeMustGenerate | NodeClobbersWorld) \
macro(ProfileType, NodeMustGenerate) \
+ macro(ProfileControlFlow, NodeMustGenerate) \
\
/* Nodes used for activations. Activation support works by having it anchored at */\
/* epilgoues via TearOffActivation, and all CreateActivation nodes kept alive by */\
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 0151daa..9d52d4f 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -647,6 +647,7 @@
case ProfileWillCall:
case ProfileDidCall:
case ProfileType:
+ case ProfileControlFlow:
case CheckHasInstance:
case ThrowReferenceError:
case ForceOSRExit:
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index 90eda2f..32c3a2b 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -200,6 +200,7 @@
case ProfileWillCall:
case ProfileDidCall:
case ProfileType:
+ case ProfileControlFlow:
case CheckHasInstance:
case InstanceOf:
case IsUndefined:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 6798a5f..e19d80d 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -4863,6 +4863,15 @@
noResult(node);
break;
}
+ case ProfileControlFlow: {
+ BasicBlockLocation* basicBlockLocation = node->basicBlockLocation();
+ if (!basicBlockLocation->hasExecuted()) {
+ GPRTemporary scratch1(this);
+ basicBlockLocation->emitExecuteCode(m_jit, scratch1.gpr());
+ }
+ noResult(node);
+ break;
+ }
case ForceOSRExit: {
terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), 0);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index c89f40e..62c5d71 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -4935,6 +4935,15 @@
noResult(node);
break;
}
+ case ProfileControlFlow: {
+ BasicBlockLocation* basicBlockLocation = node->basicBlockLocation();
+ if (!basicBlockLocation->hasExecuted()) {
+ GPRTemporary scratch1(this);
+ basicBlockLocation->emitExecuteCode(m_jit, scratch1.gpr());
+ }
+ noResult(node);
+ break;
+ }
#if ENABLE(FTL_JIT)
case CheckTierUpInLoop: {
diff --git a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp
index c00f3fe..3c9fa13 100644
--- a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp
+++ b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp
@@ -226,7 +226,7 @@
location->getInteger(ASCIILiteral("divot"), divot);
bool okay;
- TypeLocation* typeLocation = vm.typeProfiler()->findLocation(divot, sourceIDAsString.toIntPtrStrict(&okay), static_cast<TypeProfilerSearchDescriptor>(descriptor));
+ TypeLocation* typeLocation = vm.typeProfiler()->findLocation(divot, sourceIDAsString.toIntPtrStrict(&okay), static_cast<TypeProfilerSearchDescriptor>(descriptor), vm);
RefPtr<TypeSet> typeSet;
if (typeLocation) {
diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp
index f22c731..3e19c35 100644
--- a/Source/JavaScriptCore/jit/JIT.cpp
+++ b/Source/JavaScriptCore/jit/JIT.cpp
@@ -262,6 +262,7 @@
DEFINE_OP(op_profile_did_call)
DEFINE_OP(op_profile_will_call)
DEFINE_OP(op_profile_type)
+ DEFINE_OP(op_profile_control_flow)
DEFINE_OP(op_push_name_scope)
DEFINE_OP(op_push_with_scope)
case op_put_by_id_out_of_line:
diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h
index 2872de0..4ce7d98 100644
--- a/Source/JavaScriptCore/jit/JIT.h
+++ b/Source/JavaScriptCore/jit/JIT.h
@@ -526,6 +526,7 @@
void emit_op_profile_did_call(Instruction*);
void emit_op_profile_will_call(Instruction*);
void emit_op_profile_type(Instruction*);
+ void emit_op_profile_control_flow(Instruction*);
void emit_op_push_name_scope(Instruction*);
void emit_op_push_with_scope(Instruction*);
void emit_op_put_by_id(Instruction*);
diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp
index 9d791c7..637a002 100644
--- a/Source/JavaScriptCore/jit/JITOpcodes.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp
@@ -29,6 +29,7 @@
#include "JIT.h"
#include "Arguments.h"
+#include "BasicBlockLocation.h"
#include "CopiedSpaceInlines.h"
#include "Debugger.h"
#include "Heap.h"
@@ -1390,6 +1391,13 @@
jumpToEnd.link(this);
}
+void JIT::emit_op_profile_control_flow(Instruction* currentInstruction)
+{
+ BasicBlockLocation* basicBlockLocation = currentInstruction[1].u.basicBlockLocation;
+ if (!basicBlockLocation->hasExecuted())
+ basicBlockLocation->emitExecuteCode(*this, regT1);
+}
+
#endif // USE(JSVALUE64)
} // namespace JSC
diff --git a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
index 7fb8598..886cd9c 100644
--- a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
@@ -1374,6 +1374,13 @@
jumpToEnd.link(this);
}
+void JIT::emit_op_profile_control_flow(Instruction* currentInstruction)
+{
+ BasicBlockLocation* basicBlockLocation = currentInstruction[1].u.basicBlockLocation;
+ if (!basicBlockLocation->hasExecuted())
+ basicBlockLocation->emitExecuteCode(*this, regT1);
+}
+
} // namespace JSC
#endif // USE(JSVALUE32_64)
diff --git a/Source/JavaScriptCore/jsc.cpp b/Source/JavaScriptCore/jsc.cpp
index 6958703..39cdcf4 100644
--- a/Source/JavaScriptCore/jsc.cpp
+++ b/Source/JavaScriptCore/jsc.cpp
@@ -481,6 +481,7 @@
static EncodedJSValue JSC_HOST_CALL functionDumpTypesForAllVariables(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionFindTypeForExpression(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionReturnTypeFor(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionDumpBasicBlockExecutionRanges(ExecState*);
#if ENABLE(SAMPLING_FLAGS)
static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
@@ -635,6 +636,8 @@
addFunction(vm, "dumpTypesForAllVariables", functionDumpTypesForAllVariables , 0);
addFunction(vm, "findTypeForExpression", functionFindTypeForExpression, 2);
addFunction(vm, "returnTypeFor", functionReturnTypeFor, 1);
+
+ addFunction(vm, "dumpBasicBlockExecutionRanges", functionDumpBasicBlockExecutionRanges , 0);
JSArray* array = constructEmptyArray(globalExec(), 0);
for (size_t i = 0; i < arguments.size(); ++i)
@@ -1086,7 +1089,7 @@
String sourceCodeText = executable->source().toString();
unsigned offset = static_cast<unsigned>(sourceCodeText.find(substring) + executable->source().startOffset());
- String jsonString = exec->vm().typeProfiler()->typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptorNormal, offset, executable->sourceID());
+ String jsonString = exec->vm().typeProfiler()->typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptorNormal, offset, executable->sourceID(), exec->vm());
return JSValue::encode(JSONParse(exec, jsonString));
}
@@ -1100,10 +1103,17 @@
FunctionExecutable* executable = (jsDynamicCast<JSFunction*>(functionValue.asCell()->getObject()))->jsExecutable();
unsigned offset = executable->source().startOffset();
- String jsonString = exec->vm().typeProfiler()->typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptorFunctionReturn, offset, executable->sourceID());
+ String jsonString = exec->vm().typeProfiler()->typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptorFunctionReturn, offset, executable->sourceID(), exec->vm());
return JSValue::encode(JSONParse(exec, jsonString));
}
+EncodedJSValue JSC_HOST_CALL functionDumpBasicBlockExecutionRanges(ExecState* exec)
+{
+ RELEASE_ASSERT(exec->vm().controlFlowProfiler());
+ exec->vm().controlFlowProfiler()->dumpData();
+ return JSValue::encode(jsUndefined());
+}
+
// Use SEH for Release builds only to get rid of the crash report dialog
// (luckily the same tests fail in Release and Debug builds so far). Need to
// be in a separate main function because the jscmain function requires object
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
index 8b8fb0f..89bc068 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
@@ -1346,6 +1346,17 @@
callSlowPath(_slow_path_to_index_string)
dispatch(3)
+_llint_op_profile_type:
+ traceExecution()
+ callSlowPath(_slow_path_profile_type)
+ dispatch(6)
+
+_llint_op_profile_control_flow:
+ traceExecution()
+ loadpFromInstruction(1, t0)
+ storeb 1, BasicBlockLocation::m_hasExecuted[t0]
+ dispatch(2)
+
# Lastly, make sure that we can link even though we don't support all opcodes.
# These opcodes should never arise when using LLInt or either JIT. We assert
# as much.
@@ -1366,7 +1377,3 @@
_llint_op_init_global_const_nop:
dispatch(5)
-
-_llint_op_profile_type:
- callSlowPath(_slow_path_profile_type)
- dispatch(6)
diff --git a/Source/JavaScriptCore/parser/ASTBuilder.h b/Source/JavaScriptCore/parser/ASTBuilder.h
index d66fb35..c029f66 100644
--- a/Source/JavaScriptCore/parser/ASTBuilder.h
+++ b/Source/JavaScriptCore/parser/ASTBuilder.h
@@ -278,10 +278,11 @@
return node;
}
- ExpressionNode* createFunctionExpr(const JSTokenLocation& location, const Identifier* name, FunctionBodyNode* body, ParameterNode* parameters, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned startColumn)
+ ExpressionNode* createFunctionExpr(const JSTokenLocation& location, const Identifier* name, FunctionBodyNode* body, ParameterNode* parameters, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned startColumn, unsigned functionKeywordStart)
{
FuncExprNode* result = new (m_parserArena) FuncExprNode(location, *name, body, m_sourceCode->subExpression(openBraceOffset, closeBraceOffset, bodyStartLine, startColumn), parameters);
body->setLoc(bodyStartLine, bodyEndLine, location.startOffset, location.lineStartOffset);
+ body->setFunctionKeywordStart(functionKeywordStart);
return result;
}
@@ -295,17 +296,19 @@
body->setFunctionNameStart(functionNameStart);
}
- NEVER_INLINE PropertyNode* createGetterOrSetterProperty(const JSTokenLocation& location, PropertyNode::Type type, bool, const Identifier* name, ParameterNode* params, FunctionBodyNode* body, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned bodyStartColumn)
+ NEVER_INLINE PropertyNode* createGetterOrSetterProperty(const JSTokenLocation& location, PropertyNode::Type type, bool, const Identifier* name, ParameterNode* params, FunctionBodyNode* body, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned bodyStartColumn, unsigned getOrSetStartOffset)
{
ASSERT(name);
body->setLoc(bodyStartLine, bodyEndLine, location.startOffset, location.lineStartOffset);
body->setInferredName(*name);
+ body->setFunctionKeywordStart(getOrSetStartOffset);
return new (m_parserArena) PropertyNode(*name, new (m_parserArena) FuncExprNode(location, m_vm->propertyNames->nullIdentifier, body, m_sourceCode->subExpression(openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn), params), type);
}
- NEVER_INLINE PropertyNode* createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation& location, PropertyNode::Type type, bool, double name, ParameterNode* params, FunctionBodyNode* body, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned bodyStartColumn)
+ NEVER_INLINE PropertyNode* createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation& location, PropertyNode::Type type, bool, double name, ParameterNode* params, FunctionBodyNode* body, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned bodyStartColumn, unsigned getOrSetStartOffset)
{
body->setLoc(bodyStartLine, bodyEndLine, location.startOffset, location.lineStartOffset);
+ body->setFunctionKeywordStart(getOrSetStartOffset);
return new (m_parserArena) PropertyNode(parserArena.identifierArena().makeNumericIdentifier(vm, name), new (m_parserArena) FuncExprNode(location, vm->propertyNames->nullIdentifier, body, m_sourceCode->subExpression(openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn), params), type);
}
@@ -338,13 +341,14 @@
ClauseListNode* createClauseList(CaseClauseNode* clause) { return new (m_parserArena) ClauseListNode(clause); }
ClauseListNode* createClauseList(ClauseListNode* tail, CaseClauseNode* clause) { return new (m_parserArena) ClauseListNode(tail, clause); }
- StatementNode* createFuncDeclStatement(const JSTokenLocation& location, const Identifier* name, FunctionBodyNode* body, ParameterNode* parameters, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned bodyStartColumn)
+ StatementNode* createFuncDeclStatement(const JSTokenLocation& location, const Identifier* name, FunctionBodyNode* body, ParameterNode* parameters, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned bodyStartColumn, unsigned functionKeywordStart)
{
FuncDeclNode* decl = new (m_parserArena) FuncDeclNode(location, *name, body, m_sourceCode->subExpression(openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn), parameters);
if (*name == m_vm->propertyNames->arguments)
usesArguments();
m_scope.m_funcDeclarations->data.append(decl->body());
body->setLoc(bodyStartLine, bodyEndLine, location.startOffset, location.lineStartOffset);
+ body->setFunctionKeywordStart(functionKeywordStart);
return decl;
}
@@ -673,6 +677,16 @@
{
node->setEndOffset(offset);
}
+
+ int endOffset(Node* node)
+ {
+ return node->endOffset();
+ }
+
+ void setStartOffset(CaseClauseNode* node, int offset)
+ {
+ node->setStartOffset(offset);
+ }
private:
struct Scope {
diff --git a/Source/JavaScriptCore/parser/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h
index 41d0772..8e5c5fc 100644
--- a/Source/JavaScriptCore/parser/NodeConstructors.h
+++ b/Source/JavaScriptCore/parser/NodeConstructors.h
@@ -44,6 +44,7 @@
inline Node::Node(const JSTokenLocation& location)
: m_position(location.line, location.startOffset, location.lineStartOffset)
+ , m_endOffset(-1)
{
ASSERT(location.startOffset >= location.lineStartOffset);
}
diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h
index 69a476a..a22c170 100644
--- a/Source/JavaScriptCore/parser/Nodes.h
+++ b/Source/JavaScriptCore/parser/Nodes.h
@@ -1545,6 +1545,8 @@
void setFunctionNameStart(int functionNameStart) { m_functionNameStart = functionNameStart; }
int functionNameStart() const { return m_functionNameStart; }
+ void setFunctionKeywordStart(int functionKeywordStart) { m_functionKeywordStart = functionKeywordStart; }
+ int functionKeywordStart() const { return m_functionKeywordStart; }
unsigned startColumn() const { return m_startColumn; }
unsigned endColumn() const { return m_endColumn; }
@@ -1561,6 +1563,7 @@
FunctionMode m_functionMode;
RefPtr<FunctionParameters> m_parameters;
int m_functionNameStart;
+ int m_functionKeywordStart;
unsigned m_startColumn;
unsigned m_endColumn;
SourceCode m_source;
@@ -1729,10 +1732,12 @@
ExpressionNode* expr() const { return m_expr; }
void emitBytecode(BytecodeGenerator&, RegisterID* destination);
+ void setStartOffset(int offset) { m_startOffset = offset; }
private:
ExpressionNode* m_expr;
SourceElements* m_statements;
+ int m_startOffset;
};
class ClauseListNode : public ParserArenaFreeable {
diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp
index 733fdc4..c425091 100644
--- a/Source/JavaScriptCore/parser/Parser.cpp
+++ b/Source/JavaScriptCore/parser/Parser.cpp
@@ -1001,6 +1001,7 @@
{
if (!match(CASE))
return 0;
+ unsigned startOffset = tokenStart();
next();
TreeExpression condition = parseExpression(context);
failIfFalse(condition, "Cannot parse switch clause");
@@ -1008,10 +1009,12 @@
TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode);
failIfFalse(statements, "Cannot parse the body of a switch clause");
TreeClause clause = context.createClause(condition, statements);
+ context.setStartOffset(clause, startOffset);
TreeClauseList clauseList = context.createClauseList(clause);
TreeClauseList tail = clauseList;
while (match(CASE)) {
+ startOffset = tokenStart();
next();
TreeExpression condition = parseExpression(context);
failIfFalse(condition, "Cannot parse switch case expression");
@@ -1019,6 +1022,7 @@
TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode);
failIfFalse(statements, "Cannot parse the body of a switch clause");
clause = context.createClause(condition, statements);
+ context.setStartOffset(clause, startOffset);
tail = context.createClauseList(tail, clause);
}
return clauseList;
@@ -1029,11 +1033,14 @@
{
if (!match(DEFAULT))
return 0;
+ unsigned startOffset = tokenStart();
next();
consumeOrFail(COLON, "Expected a ':' after switch default clause");
TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode);
failIfFalse(statements, "Cannot parse the body of a switch default clause");
- return context.createClause(0, statements);
+ TreeClause result = context.createClause(0, statements);
+ context.setStartOffset(result, startOffset);
+ return result;
}
template <typename LexerType>
@@ -1106,14 +1113,20 @@
int start = tokenLine();
next();
if (match(CLOSEBRACE)) {
+ unsigned endOffset = m_lexer->currentOffset();
next();
- return context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line);
+ TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line);
+ context.setEndOffset(result, endOffset);
+ return result;
}
TreeSourceElements subtree = parseSourceElements(context, DontCheckForStrictMode);
failIfFalse(subtree, "Cannot parse the body of the block statement");
matchOrFail(CLOSEBRACE, "Expected a closing '}' at the end of a block statement");
+ unsigned endOffset = m_lexer->currentOffset();
next();
- return context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line);
+ TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line);
+ context.setEndOffset(result, endOffset);
+ return result;
}
template <typename LexerType>
@@ -1205,7 +1218,7 @@
}
if (result)
- context.setEndOffset(result, m_lexer->currentOffset());
+ context.setEndOffset(result, m_lastTokenEndPosition.offset);
return result;
}
@@ -1375,6 +1388,7 @@
{
ASSERT(match(FUNCTION));
JSTokenLocation location(tokenLocation());
+ unsigned functionKeywordStart = tokenStart();
next();
const Identifier* name = 0;
TreeFormalParameterList parameters = 0;
@@ -1386,7 +1400,7 @@
failIfFalse((parseFunctionInfo(context, FunctionNeedsName, FunctionMode, true, name, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn)), "Cannot parse this function");
failIfFalse(name, "Function statements must have a name");
failIfFalseIfStrict(declareVariable(name), "Cannot declare a function named '", name->impl(), "' in strict mode");
- return context.createFuncDeclStatement(location, name, body, parameters, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn);
+ return context.createFuncDeclStatement(location, name, body, parameters, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn, functionKeywordStart);
}
struct LabelInfo {
@@ -1541,7 +1555,9 @@
posStack.removeLast();
JSTokenLocation elseLocation = tokenLocationStack.last();
tokenLocationStack.removeLast();
- statementStack.append(context.createIfStatement(elseLocation, condition, trueBlock, 0, pos.first, pos.second));
+ TreeStatement ifStatement = context.createIfStatement(elseLocation, condition, trueBlock, 0, pos.first, pos.second);
+ context.setEndOffset(ifStatement, context.endOffset(trueBlock));
+ statementStack.append(ifStatement);
}
while (!exprStack.isEmpty()) {
@@ -1555,7 +1571,9 @@
posStack.removeLast();
JSTokenLocation elseLocation = tokenLocationStack.last();
tokenLocationStack.removeLast();
- statementStack.append(context.createIfStatement(elseLocation, condition, trueBlock, falseBlock, pos.first, pos.second));
+ TreeStatement ifStatement = context.createIfStatement(elseLocation, condition, trueBlock, falseBlock, pos.first, pos.second);
+ context.setEndOffset(ifStatement, context.endOffset(falseBlock));
+ statementStack.append(ifStatement);
}
return context.createIfStatement(ifLocation, condition, trueBlock, statementStack.last(), start, end);
@@ -1568,7 +1586,7 @@
JSTokenLocation location(tokenLocation());
TreeExpression node = parseAssignmentExpression(context);
failIfFalse(node, "Cannot parse expression");
- context.setEndOffset(node, m_lexer->currentOffset());
+ context.setEndOffset(node, m_lastTokenEndPosition.offset);
if (!match(COMMA))
return node;
next();
@@ -1576,16 +1594,16 @@
m_nonLHSCount++;
TreeExpression right = parseAssignmentExpression(context);
failIfFalse(right, "Cannot parse expression in a comma expression");
- context.setEndOffset(right, m_lexer->currentOffset());
+ context.setEndOffset(right, m_lastTokenEndPosition.offset);
typename TreeBuilder::Comma commaNode = context.createCommaExpr(location, node, right);
while (match(COMMA)) {
next(TreeBuilder::DontBuildStrings);
right = parseAssignmentExpression(context);
failIfFalse(right, "Cannot parse expression in a comma expression");
- context.setEndOffset(right, m_lexer->currentOffset());
+ context.setEndOffset(right, m_lastTokenEndPosition.offset);
context.appendToComma(commaNode, right);
}
- context.setEndOffset(commaNode, m_lexer->currentOffset());
+ context.setEndOffset(commaNode, m_lastTokenEndPosition.offset);
return commaNode;
}
@@ -1682,10 +1700,12 @@
next(TreeBuilder::DontBuildStrings);
TreeExpression lhs = parseAssignmentExpression(context);
failIfFalse(lhs, "Cannot parse left hand side of ternary operator");
+ context.setEndOffset(lhs, m_lexer->currentOffset());
consumeOrFailWithFlags(COLON, TreeBuilder::DontBuildStrings, "Expected ':' in ternary operator");
TreeExpression rhs = parseAssignmentExpression(context);
failIfFalse(rhs, "Cannot parse right hand side of ternary operator");
+ context.setEndOffset(rhs, m_lexer->currentOffset());
return context.createConditionalExpr(location, cond, lhs, rhs);
}
@@ -1759,6 +1779,7 @@
FALLTHROUGH;
case STRING: {
const Identifier* ident = m_token.m_data.ident;
+ unsigned getterOrSetterStartOffset = tokenStart();
if (complete || (wasIdent && (*ident == m_vm->propertyNames->get || *ident == m_vm->propertyNames->set)))
nextExpectIdentifier(LexerFlagsIgnoreReservedWords);
else
@@ -1804,8 +1825,8 @@
failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SetterMode, false, accessorName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn)), "Cannot parse setter definition");
}
if (stringPropertyName)
- return context.createGetterOrSetterProperty(location, type, complete, stringPropertyName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn);
- return context.createGetterOrSetterProperty(const_cast<VM*>(m_vm), m_parserArena, location, type, complete, numericPropertyName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn);
+ return context.createGetterOrSetterProperty(location, type, complete, stringPropertyName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn, getterOrSetterStartOffset);
+ return context.createGetterOrSetterProperty(const_cast<VM*>(m_vm), m_parserArena, location, type, complete, numericPropertyName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn, getterOrSetterStartOffset);
}
case NUMBER: {
double propertyName = m_token.m_data.doubleValue;
@@ -2154,10 +2175,11 @@
unsigned closeBraceOffset = 0;
int bodyStartLine = 0;
unsigned bodyStartColumn = 0;
+ unsigned functionKeywordStart = tokenStart();
location = tokenLocation();
next();
failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, FunctionMode, false, name, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn)), "Cannot parse function expression");
- base = context.createFunctionExpr(location, name, body, parameters, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn);
+ base = context.createFunctionExpr(location, name, body, parameters, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn, functionKeywordStart);
} else
base = parsePrimaryExpression(context);
diff --git a/Source/JavaScriptCore/parser/SyntaxChecker.h b/Source/JavaScriptCore/parser/SyntaxChecker.h
index 898c42e..03297f2 100644
--- a/Source/JavaScriptCore/parser/SyntaxChecker.h
+++ b/Source/JavaScriptCore/parser/SyntaxChecker.h
@@ -159,7 +159,7 @@
ExpressionType createConditionalExpr(const JSTokenLocation&, ExpressionType, ExpressionType, ExpressionType) { return ConditionalExpr; }
ExpressionType createAssignResolve(const JSTokenLocation&, const Identifier&, ExpressionType, int, int, int) { return AssignmentExpr; }
ExpressionType createEmptyVarExpression(const JSTokenLocation&, const Identifier&) { return AssignmentExpr; }
- ExpressionType createFunctionExpr(const JSTokenLocation&, const Identifier*, int, int, int, int, int, int, int) { return FunctionExpr; }
+ ExpressionType createFunctionExpr(const JSTokenLocation&, const Identifier*, int, int, int, int, int, int, int, int) { return FunctionExpr; }
int createFunctionBody(const JSTokenLocation&, const JSTokenLocation&, int, int, bool) { return FunctionBodyResult; }
void setFunctionNameStart(int, int) { }
int createArguments() { return ArgumentsResult; }
@@ -193,7 +193,7 @@
int createClause(int, int) { return ClauseResult; }
int createClauseList(int) { return ClauseListResult; }
int createClauseList(int, int) { return ClauseListResult; }
- int createFuncDeclStatement(const JSTokenLocation&, const Identifier*, int, int, int, int, int, int, int) { return StatementResult; }
+ int createFuncDeclStatement(const JSTokenLocation&, const Identifier*, int, int, int, int, int, int, int, int) { return StatementResult; }
int createBlockStatement(const JSTokenLocation&, int, int, int) { return StatementResult; }
int createExprStatement(const JSTokenLocation&, int, int, int) { return StatementResult; }
int createIfStatement(const JSTokenLocation&, int, int, int, int) { return StatementResult; }
@@ -218,14 +218,14 @@
int createDebugger(const JSTokenLocation&, int, int) { return StatementResult; }
int createConstStatement(const JSTokenLocation&, int, int, int) { return StatementResult; }
int appendConstDecl(const JSTokenLocation&, int, const Identifier*, int) { return StatementResult; }
- Property createGetterOrSetterProperty(const JSTokenLocation&, PropertyNode::Type type, bool strict, const Identifier* name, int, int, int, int, int, int, int)
+ Property createGetterOrSetterProperty(const JSTokenLocation&, PropertyNode::Type type, bool strict, const Identifier* name, int, int, int, int, int, int, int, int)
{
ASSERT(name);
if (!strict)
return Property(type);
return Property(name, type);
}
- Property createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation&, PropertyNode::Type type, bool strict, double name, int, int, int, int, int, int, int)
+ Property createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation&, PropertyNode::Type type, bool strict, double name, int, int, int, int, int, int, int, int)
{
if (!strict)
return Property(type);
@@ -297,6 +297,8 @@
}
void setEndOffset(int, int) { }
+ int endOffset(int) { return 0; }
+ void setStartOffset(int, int) { }
private:
int m_topBinaryExpr;
diff --git a/Source/JavaScriptCore/runtime/BasicBlockLocation.cpp b/Source/JavaScriptCore/runtime/BasicBlockLocation.cpp
new file mode 100644
index 0000000..16e4da3
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/BasicBlockLocation.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014 Saam Barati. <saambarati1@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "BasicBlockLocation.h"
+
+#include "CCallHelpers.h"
+
+namespace JSC {
+
+BasicBlockLocation::BasicBlockLocation(int startOffset, int endOffset)
+ : m_startOffset(startOffset)
+ , m_endOffset(endOffset)
+ , m_hasExecuted(false)
+{
+}
+
+void BasicBlockLocation::insertGap(int startOffset, int endOffset)
+{
+ std::pair<int, int> gap(startOffset, endOffset);
+ if (!m_gaps.contains(gap))
+ m_gaps.append(gap);
+}
+
+Vector<std::pair<int, int>> BasicBlockLocation::getExecutedRanges() const
+{
+ Vector<Gap> result;
+ Vector<Gap> gaps = m_gaps;
+ int nextRangeStart = m_startOffset;
+ while (gaps.size()) {
+ Gap minGap(INT_MAX, 0);
+ unsigned minIdx = -1;
+ for (unsigned idx = 0; idx < gaps.size(); idx++) {
+ // Because we know that the Gaps inside m_gaps aren't enclosed within one another, it suffices to just check the first element to test ordering.
+ if (gaps[idx].first < minGap.first) {
+ minGap = gaps[idx];
+ minIdx = idx;
+ }
+ }
+ result.append(Gap(nextRangeStart, minGap.first));
+ nextRangeStart = minGap.second;
+ gaps.remove(minIdx);
+ }
+
+ result.append(Gap(nextRangeStart, m_endOffset));
+ return result;
+}
+
+void BasicBlockLocation::dumpData() const
+{
+ Vector<Gap> executedRanges = getExecutedRanges();
+ for (Gap gap : executedRanges)
+ dataLogF("\tBasicBlock: [%d, %d] hasExecuted: %s\n", gap.first, gap.second, hasExecuted() ? "true" : "false");
+}
+
+#if ENABLE(JIT)
+void BasicBlockLocation::emitExecuteCode(CCallHelpers& jit, MacroAssembler::RegisterID ptrReg) const
+{
+ jit.move(CCallHelpers::TrustedImmPtr(&m_hasExecuted), ptrReg);
+ jit.store8(CCallHelpers::TrustedImm32(true), CCallHelpers::Address(ptrReg, 0));
+}
+#endif // ENABLE(JIT)
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/BasicBlockLocation.h b/Source/JavaScriptCore/runtime/BasicBlockLocation.h
new file mode 100644
index 0000000..49bf514
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/BasicBlockLocation.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014 Saam Barati. <saambarati1@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BasicBlockLocation_h
+#define BasicBlockLocation_h
+
+#include "MacroAssembler.h"
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+class CCallHelpers;
+class LLIntOffsetsExtractor;
+
+class BasicBlockLocation {
+public:
+ typedef std::pair<int, int> Gap;
+
+ BasicBlockLocation(int startOffset = -1, int endOffset = -1);
+
+ int startOffset() const { return m_startOffset; }
+ int endOffset() const { return m_endOffset; }
+ void setStartOffset(int startOffset) { m_startOffset = startOffset; }
+ void setEndOffset(int endOffset) { m_endOffset = endOffset; }
+ bool hasExecuted() const { return m_hasExecuted; }
+ void insertGap(int, int);
+ Vector<Gap> getExecutedRanges() const;
+ JS_EXPORT_PRIVATE void dumpData() const;
+#if ENABLE(JIT)
+ void emitExecuteCode(CCallHelpers&, MacroAssembler::RegisterID) const;
+#endif
+
+private:
+ friend class LLIntOffsetsExtractor;
+
+ int m_startOffset;
+ int m_endOffset;
+ bool m_hasExecuted;
+ Vector<Gap> m_gaps;
+};
+
+} // namespace JSC
+
+#endif // BasicBlockLocation_h
diff --git a/Source/JavaScriptCore/runtime/CodeCache.cpp b/Source/JavaScriptCore/runtime/CodeCache.cpp
index c170aac..9bc28ca 100644
--- a/Source/JavaScriptCore/runtime/CodeCache.cpp
+++ b/Source/JavaScriptCore/runtime/CodeCache.cpp
@@ -79,7 +79,7 @@
{
SourceCodeKey key = SourceCodeKey(source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, strictness);
CodeCacheMap::AddResult addResult = m_sourceCode.add(key, SourceCodeValue());
- bool canCache = debuggerMode == DebuggerOff && profilerMode == ProfilerOff && !vm.typeProfiler();
+ bool canCache = debuggerMode == DebuggerOff && profilerMode == ProfilerOff && !vm.typeProfiler() && !vm.controlFlowProfiler();
if (!addResult.isNewEntry && canCache) {
UnlinkedCodeBlockType* unlinkedCodeBlock = jsCast<UnlinkedCodeBlockType*>(addResult.iterator->value.cell.get());
unsigned firstLine = source.firstLine() + unlinkedCodeBlock->firstLine();
diff --git a/Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp b/Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp
new file mode 100644
index 0000000..48a766a
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014 Saam Barati. <saambarati1@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ControlFlowProfiler.h"
+
+#include "VM.h"
+
+namespace JSC {
+
+ControlFlowProfiler::~ControlFlowProfiler()
+{
+ for (const BlockLocationCache& cache : m_sourceIDBuckets.values()) {
+ for (BasicBlockLocation* block : cache.values())
+ delete block;
+ }
+}
+
+BasicBlockLocation* ControlFlowProfiler::getBasicBlockLocation(intptr_t sourceID, int startOffset, int endOffset)
+{
+ auto addResult = m_sourceIDBuckets.add(sourceID, BlockLocationCache());
+ BlockLocationCache& blockLocationCache = addResult.iterator->value;
+ BasicBlockKey key(startOffset, endOffset);
+ auto addResultForBasicBlock = blockLocationCache.add(key, nullptr);
+ if (addResultForBasicBlock.isNewEntry)
+ addResultForBasicBlock.iterator->value = new BasicBlockLocation(startOffset, endOffset);
+ return addResultForBasicBlock.iterator->value;
+}
+
+void ControlFlowProfiler::dumpData() const
+{
+ auto iter = m_sourceIDBuckets.begin();
+ auto end = m_sourceIDBuckets.end();
+ for (; iter != end; ++iter) {
+ dataLogF("SourceID: %ld\n", iter->key);
+ for (const BasicBlockLocation* block : iter->value.values())
+ block->dumpData();
+ }
+}
+
+Vector<BasicBlockRange> ControlFlowProfiler::getBasicBlocksForSourceID(intptr_t sourceID, VM& vm) const
+{
+ Vector<BasicBlockRange> result(0);
+ auto bucketFindResult = m_sourceIDBuckets.find(sourceID);
+ if (bucketFindResult == m_sourceIDBuckets.end())
+ return result;
+
+ const BlockLocationCache& cache = bucketFindResult->value;
+ for (const BasicBlockLocation* block : cache.values()) {
+ bool hasExecuted = block->hasExecuted();
+ const Vector<BasicBlockLocation::Gap>& blockRanges = block->getExecutedRanges();
+ for (BasicBlockLocation::Gap gap : blockRanges) {
+ BasicBlockRange range;
+ range.m_hasExecuted = hasExecuted;
+ range.m_startOffset = gap.first;
+ range.m_endOffset = gap.second;
+ result.append(range);
+ }
+ }
+
+ const Vector<std::pair<unsigned, unsigned>>& unexecutedFunctionRanges = vm.functionHasExecutedCache()->getUnexecutedFunctionRanges(sourceID);
+ for (const auto& functionRange : unexecutedFunctionRanges) {
+ BasicBlockRange range;
+ range.m_hasExecuted = false;
+ range.m_startOffset = static_cast<int>(functionRange.first);
+ range.m_endOffset = static_cast<int>(functionRange.second + 1);
+ result.append(range);
+ }
+
+ return result;
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/ControlFlowProfiler.h b/Source/JavaScriptCore/runtime/ControlFlowProfiler.h
new file mode 100644
index 0000000..61f30e2
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/ControlFlowProfiler.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014 Saam Barati. <saambarati1@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ControlFlowProfiler_h
+#define ControlFlowProfiler_h
+
+#include "BasicBlockLocation.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashMethod.h>
+
+namespace JSC {
+
+class VM;
+
+struct BasicBlockKey {
+ BasicBlockKey()
+ : m_startOffset(-3)
+ , m_endOffset(-3)
+ { }
+
+ BasicBlockKey(int startOffset, int endOffset)
+ : m_startOffset(startOffset)
+ , m_endOffset(endOffset)
+ { }
+
+ BasicBlockKey(WTF::HashTableDeletedValueType)
+ : m_startOffset(-2)
+ , m_endOffset(-2)
+ { }
+
+ bool isHashTableDeletedValue() const { return m_startOffset == -2 && m_endOffset == -2; }
+ bool operator==(const BasicBlockKey& other) const { return m_startOffset == other.m_startOffset && m_endOffset == other.m_endOffset; }
+ unsigned hash() const { return m_startOffset + m_endOffset + 1; }
+
+ int m_startOffset;
+ int m_endOffset;
+};
+
+struct BasicBlockKeyHash {
+ static unsigned hash(const BasicBlockKey& key) { return key.hash(); }
+ static bool equal(const BasicBlockKey& a, const BasicBlockKey& b) { return a == b; }
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+} // namespace JSC
+
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+template<> struct DefaultHash<JSC::BasicBlockKey> {
+ typedef JSC::BasicBlockKeyHash Hash;
+};
+
+template<typename T> struct HashTraits;
+template<> struct HashTraits<JSC::BasicBlockKey> : SimpleClassHashTraits<JSC::BasicBlockKey> {
+ static const bool emptyValueIsZero = false;
+};
+
+} // namespace WTF
+
+namespace JSC {
+
+struct BasicBlockRange {
+ int m_startOffset;
+ int m_endOffset;
+ bool m_hasExecuted;
+};
+
+class ControlFlowProfiler {
+public:
+ ~ControlFlowProfiler();
+ BasicBlockLocation* getBasicBlockLocation(intptr_t sourceID, int startOffset, int endOffset);
+ JS_EXPORT_PRIVATE void dumpData() const;
+ Vector<BasicBlockRange> getBasicBlocksForSourceID(intptr_t sourceID, VM&) const;
+
+private:
+ typedef HashMap<BasicBlockKey, BasicBlockLocation*> BlockLocationCache;
+ typedef HashMap<intptr_t, BlockLocationCache> SourceIDBuckets;
+
+ SourceIDBuckets m_sourceIDBuckets;
+};
+
+} // namespace JSC
+
+#endif // ControlFlowProfiler_h
diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp
index d2ca58c..ebdcabf 100644
--- a/Source/JavaScriptCore/runtime/Executable.cpp
+++ b/Source/JavaScriptCore/runtime/Executable.cpp
@@ -378,8 +378,8 @@
{
m_typeProfilingStartOffset = 0;
m_typeProfilingEndOffset = source.length() - 1;
- if (exec->vm().typeProfiler())
- exec->vm().typeProfiler()->functionHasExecutedCache()->insertUnexecutedRange(sourceID(), m_typeProfilingStartOffset, m_typeProfilingEndOffset);
+ if (exec->vm().typeProfiler() || exec->vm().controlFlowProfiler())
+ exec->vm().functionHasExecutedCache()->insertUnexecutedRange(sourceID(), m_typeProfilingStartOffset, m_typeProfilingEndOffset);
}
void ProgramExecutable::destroy(JSCell* cell)
@@ -501,8 +501,8 @@
UnlinkedFunctionExecutable* unlinkedFunctionExecutable = functionDeclarations[i].second.get();
JSValue value = JSFunction::create(vm, unlinkedFunctionExecutable->link(vm, m_source, lineNo()), scope);
globalObject->addFunction(callFrame, functionDeclarations[i].first, value);
- if (vm.typeProfiler()) {
- vm.typeProfiler()->functionHasExecutedCache()->insertUnexecutedRange(sourceID(),
+ if (vm.typeProfiler() || vm.controlFlowProfiler()) {
+ vm.functionHasExecutedCache()->insertUnexecutedRange(sourceID(),
unlinkedFunctionExecutable->typeProfilingStartOffset(),
unlinkedFunctionExecutable->typeProfilingEndOffset());
}
diff --git a/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.cpp b/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.cpp
index a5ea85d..b96bbf2 100644
--- a/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.cpp
+++ b/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.cpp
@@ -80,4 +80,22 @@
map[range] = true;
}
+Vector<std::pair<unsigned, unsigned>> FunctionHasExecutedCache::getUnexecutedFunctionRanges(intptr_t id)
+{
+ Vector<std::pair<unsigned, unsigned>> ranges(0);
+ auto findResult = m_rangeMap.find(id);
+ if (findResult == m_rangeMap.end())
+ return ranges;
+
+ RangeMap& map = m_rangeMap.find(id)->second;
+ for (auto iter = map.begin(), end = map.end(); iter != end; ++iter) {
+ if (!iter->second) {
+ const FunctionRange& range = iter->first;
+ ranges.append(std::pair<unsigned, unsigned>(range.m_start, range.m_end));
+ }
+ }
+
+ return ranges;
+}
+
} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.h b/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.h
index 8c769f9..8fe440d 100644
--- a/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.h
+++ b/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.h
@@ -28,6 +28,7 @@
#include <unordered_map>
#include <wtf/HashMethod.h>
+#include <wtf/Vector.h>
namespace JSC {
@@ -51,6 +52,7 @@
bool hasExecutedAtOffset(intptr_t id, unsigned offset);
void insertUnexecutedRange(intptr_t id, unsigned start, unsigned end);
void removeUnexecutedRange(intptr_t id, unsigned start, unsigned end);
+ Vector<std::pair<unsigned, unsigned>> getUnexecutedFunctionRanges(intptr_t id);
private:
typedef std::unordered_map<FunctionRange, bool, HashMethod<FunctionRange>> RangeMap;
diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h
index e022b43..2b9e2b8 100644
--- a/Source/JavaScriptCore/runtime/Options.h
+++ b/Source/JavaScriptCore/runtime/Options.h
@@ -282,6 +282,7 @@
v(bool, recordGCPauseTimes, false) \
v(bool, logHeapStatisticsAtExit, false) \
v(bool, enableTypeProfiler, false) \
+ v(bool, enableControlFlowProfiler, false) \
\
v(bool, verifyHeap, false) \
v(unsigned, numberOfGCCyclesToRecordForVerification, 3) \
diff --git a/Source/JavaScriptCore/runtime/TypeProfiler.cpp b/Source/JavaScriptCore/runtime/TypeProfiler.cpp
index 4df8f73..e5bac86 100644
--- a/Source/JavaScriptCore/runtime/TypeProfiler.cpp
+++ b/Source/JavaScriptCore/runtime/TypeProfiler.cpp
@@ -38,13 +38,13 @@
{
}
-void TypeProfiler::logTypesForTypeLocation(TypeLocation* location)
+void TypeProfiler::logTypesForTypeLocation(TypeLocation* location, VM& vm)
{
TypeProfilerSearchDescriptor descriptor = location->m_globalVariableID == TypeProfilerReturnStatement ? TypeProfilerSearchDescriptorFunctionReturn : TypeProfilerSearchDescriptorNormal;
dataLogF("[Start, End]::[%u, %u]\n", location->m_divotStart, location->m_divotEnd);
- if (findLocation(location->m_divotStart, location->m_sourceID, descriptor))
+ if (findLocation(location->m_divotStart, location->m_sourceID, descriptor, vm))
dataLog("\t\t[Entry IS in System]\n");
else
dataLog("\t\t[Entry IS NOT in system]\n");
@@ -70,13 +70,13 @@
bucket.append(location);
}
-String TypeProfiler::typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptor descriptor, unsigned offset, intptr_t sourceID)
+String TypeProfiler::typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptor descriptor, unsigned offset, intptr_t sourceID, VM& vm)
{
// This returns a JSON string representing an Object with the following properties:
// globalTypeSet: 'JSON<TypeSet> | null'
// instructionTypeSet: 'JSON<TypeSet>'
- TypeLocation* location = findLocation(offset, sourceID, descriptor);
+ TypeLocation* location = findLocation(offset, sourceID, descriptor, vm);
ASSERT(location);
StringBuilder json;
@@ -106,17 +106,18 @@
return json.toString();
}
-TypeLocation* TypeProfiler::findLocation(unsigned divot, intptr_t sourceID, TypeProfilerSearchDescriptor descriptor)
+TypeLocation* TypeProfiler::findLocation(unsigned divot, intptr_t sourceID, TypeProfilerSearchDescriptor descriptor, VM& vm)
{
QueryKey queryKey(sourceID, divot);
auto iter = m_queryCache.find(queryKey);
if (iter != m_queryCache.end())
return iter->value;
- if (!m_functionHasExecutedCache.hasExecutedAtOffset(sourceID, divot))
+ if (!vm.functionHasExecutedCache()->hasExecutedAtOffset(sourceID, divot))
return nullptr;
- ASSERT(m_bucketMap.contains(sourceID));
+ if (!m_bucketMap.contains(sourceID))
+ return nullptr;
Vector<TypeLocation*>& bucket = m_bucketMap.find(sourceID)->value;
TypeLocation* bestMatch = nullptr;
@@ -156,11 +157,11 @@
}
}
-void TypeProfiler::dumpTypeProfilerData()
+void TypeProfiler::dumpTypeProfilerData(VM& vm)
{
for (Bag<TypeLocation>::iterator iter = m_typeLocationInfo.begin(); !!iter; ++iter) {
TypeLocation* location = *iter;
- logTypesForTypeLocation(location);
+ logTypesForTypeLocation(location, vm);
}
}
diff --git a/Source/JavaScriptCore/runtime/TypeProfiler.h b/Source/JavaScriptCore/runtime/TypeProfiler.h
index d5894d6..34f1b8d 100644
--- a/Source/JavaScriptCore/runtime/TypeProfiler.h
+++ b/Source/JavaScriptCore/runtime/TypeProfiler.h
@@ -27,7 +27,6 @@
#define TypeProfiler_h
#include "CodeBlock.h"
-#include "FunctionHasExecutedCache.h"
#include "TypeLocation.h"
#include "TypeLocationCache.h"
#include <wtf/Bag.h>
@@ -87,6 +86,8 @@
namespace JSC {
+class VM;
+
enum TypeProfilerSearchDescriptor {
TypeProfilerSearchDescriptorNormal = 1,
TypeProfilerSearchDescriptorFunctionReturn = 2
@@ -95,21 +96,19 @@
class TypeProfiler {
public:
TypeProfiler();
- void logTypesForTypeLocation(TypeLocation*);
- JS_EXPORT_PRIVATE String typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptor, unsigned offset, intptr_t sourceID);
+ void logTypesForTypeLocation(TypeLocation*, VM&);
+ JS_EXPORT_PRIVATE String typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptor, unsigned offset, intptr_t sourceID, VM&);
void insertNewLocation(TypeLocation*);
- FunctionHasExecutedCache* functionHasExecutedCache() { return &m_functionHasExecutedCache; }
TypeLocationCache* typeLocationCache() { return &m_typeLocationCache; }
- TypeLocation* findLocation(unsigned divot, intptr_t sourceID, TypeProfilerSearchDescriptor);
+ TypeLocation* findLocation(unsigned divot, intptr_t sourceID, TypeProfilerSearchDescriptor, VM&);
GlobalVariableID getNextUniqueVariableID() { return m_nextUniqueVariableID++; }
TypeLocation* nextTypeLocation();
void invalidateTypeSetCache();
- void dumpTypeProfilerData();
+ void dumpTypeProfilerData(VM&);
private:
typedef HashMap<intptr_t, Vector<TypeLocation*>> SourceIDToLocationBucketMap;
SourceIDToLocationBucketMap m_bucketMap;
- FunctionHasExecutedCache m_functionHasExecutedCache;
TypeLocationCache m_typeLocationCache;
typedef HashMap<QueryKey, TypeLocation*> TypeLocationQueryCache;
TypeLocationQueryCache m_queryCache;
diff --git a/Source/JavaScriptCore/runtime/VM.cpp b/Source/JavaScriptCore/runtime/VM.cpp
index c3c466c..ba0e0b4 100644
--- a/Source/JavaScriptCore/runtime/VM.cpp
+++ b/Source/JavaScriptCore/runtime/VM.cpp
@@ -190,6 +190,7 @@
, m_enabledProfiler(nullptr)
, m_builtinExecutables(std::make_unique<BuiltinExecutables>(*this))
, m_typeProfilerEnabledCount(0)
+ , m_controlFlowProfilerEnabledCount(0)
{
interpreter = new Interpreter(*this);
StackBounds stack = wtfThreadData().stack();
@@ -281,6 +282,8 @@
if (Options::enableTypeProfiler())
enableTypeProfiler();
+ if (Options::enableControlFlowProfiler())
+ enableControlFlowProfiler();
}
VM::~VM()
@@ -893,41 +896,76 @@
}
}
-bool VM::enableTypeProfiler()
+static bool enableProfilerWithRespectToCount(unsigned& counter, std::function<void()> doEnableWork)
{
bool needsToRecompile = false;
- if (!m_typeProfilerEnabledCount) {
- m_typeProfiler = std::make_unique<TypeProfiler>();
- m_typeProfilerLog = std::make_unique<TypeProfilerLog>();
+ if (!counter) {
+ doEnableWork();
needsToRecompile = true;
}
- m_typeProfilerEnabledCount++;
+ counter++;
return needsToRecompile;
}
-bool VM::disableTypeProfiler()
+static bool disableProfilerWithRespectToCount(unsigned& counter, std::function<void()> doDisableWork)
{
- RELEASE_ASSERT(m_typeProfilerEnabledCount > 0);
-
+ RELEASE_ASSERT(counter > 0);
bool needsToRecompile = false;
- m_typeProfilerEnabledCount--;
- if (!m_typeProfilerEnabledCount) {
- m_typeProfiler.reset(nullptr);
- m_typeProfilerLog.reset(nullptr);
+ counter--;
+ if (!counter) {
+ doDisableWork();
needsToRecompile = true;
}
return needsToRecompile;
}
+bool VM::enableTypeProfiler()
+{
+ auto enableTypeProfiler = [this] () {
+ this->m_typeProfiler = std::make_unique<TypeProfiler>();
+ this->m_typeProfilerLog = std::make_unique<TypeProfilerLog>();
+ };
+
+ return enableProfilerWithRespectToCount(m_typeProfilerEnabledCount, enableTypeProfiler);
+}
+
+bool VM::disableTypeProfiler()
+{
+ auto disableTypeProfiler = [this] () {
+ this->m_typeProfiler.reset(nullptr);
+ this->m_typeProfilerLog.reset(nullptr);
+ };
+
+ return disableProfilerWithRespectToCount(m_typeProfilerEnabledCount, disableTypeProfiler);
+}
+
+bool VM::enableControlFlowProfiler()
+{
+ auto enableControlFlowProfiler = [this] () {
+ this->m_controlFlowProfiler = std::make_unique<ControlFlowProfiler>();
+ };
+
+ return enableProfilerWithRespectToCount(m_controlFlowProfilerEnabledCount, enableControlFlowProfiler);
+}
+
+bool VM::disableControlFlowProfiler()
+{
+ auto disableControlFlowProfiler = [this] () {
+ this->m_controlFlowProfiler.reset(nullptr);
+ };
+
+ return disableProfilerWithRespectToCount(m_controlFlowProfilerEnabledCount, disableControlFlowProfiler);
+}
+
void VM::dumpTypeProfilerData()
{
if (!typeProfiler())
return;
typeProfilerLog()->processLogEntries(ASCIILiteral("VM Dump Types"));
- typeProfiler()->dumpTypeProfilerData();
+ typeProfiler()->dumpTypeProfilerData(*this);
}
void sanitizeStackForVM(VM* vm)
diff --git a/Source/JavaScriptCore/runtime/VM.h b/Source/JavaScriptCore/runtime/VM.h
index 26a2964..6cb9aed 100644
--- a/Source/JavaScriptCore/runtime/VM.h
+++ b/Source/JavaScriptCore/runtime/VM.h
@@ -29,8 +29,10 @@
#ifndef VM_h
#define VM_h
+#include "ControlFlowProfiler.h"
#include "DateInstanceCache.h"
#include "ExecutableAllocator.h"
+#include "FunctionHasExecutedCache.h"
#include "Heap.h"
#include "Intrinsic.h"
#include "JITThunks.h"
@@ -517,6 +519,12 @@
TypeProfiler* typeProfiler() { return m_typeProfiler.get(); }
JS_EXPORT_PRIVATE void dumpTypeProfilerData();
+ FunctionHasExecutedCache* functionHasExecutedCache() { return &m_functionHasExecutedCache; }
+
+ JS_EXPORT_PRIVATE ControlFlowProfiler* controlFlowProfiler() { return m_controlFlowProfiler.get(); }
+ bool enableControlFlowProfiler();
+ bool disableControlFlowProfiler();
+
private:
friend class LLIntOffsetsExtractor;
friend class ClearExceptionScope;
@@ -568,6 +576,9 @@
std::unique_ptr<TypeProfiler> m_typeProfiler;
std::unique_ptr<TypeProfilerLog> m_typeProfilerLog;
unsigned m_typeProfilerEnabledCount;
+ FunctionHasExecutedCache m_functionHasExecutedCache;
+ std::unique_ptr<ControlFlowProfiler> m_controlFlowProfiler;
+ unsigned m_controlFlowProfilerEnabledCount;
};
#if ENABLE(GC_VALIDATION)