FTL should inline polymorphic heap accesses
https://bugs.webkit.org/show_bug.cgi?id=128795
Source/JavaScriptCore:
Reviewed by Oliver Hunt.
We now inline GetByIds that we know are pure but polymorphic. They manifest in DFG IR
as MultiGetByOffset, and in LLVM IR as a switch with a basic block for each kind of
read.
2% speed-up on Octane mostly due to a 18% speed-up on deltablue.
* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecode/ExitingJITType.cpp: Added.
(WTF::printInternal):
* bytecode/ExitingJITType.h:
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeFromLLInt):
(JSC::GetByIdStatus::computeForChain):
(JSC::GetByIdStatus::computeForStubInfo):
(JSC::GetByIdStatus::computeFor):
(JSC::GetByIdStatus::dump):
* bytecode/GetByIdStatus.h:
(JSC::GetByIdStatus::GetByIdStatus):
(JSC::GetByIdStatus::numVariants):
(JSC::GetByIdStatus::variants):
(JSC::GetByIdStatus::at):
(JSC::GetByIdStatus::operator[]):
* bytecode/GetByIdVariant.cpp: Added.
(JSC::GetByIdVariant::dump):
(JSC::GetByIdVariant::dumpInContext):
* bytecode/GetByIdVariant.h: Added.
(JSC::GetByIdVariant::GetByIdVariant):
(JSC::GetByIdVariant::isSet):
(JSC::GetByIdVariant::operator!):
(JSC::GetByIdVariant::structureSet):
(JSC::GetByIdVariant::chain):
(JSC::GetByIdVariant::specificValue):
(JSC::GetByIdVariant::offset):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::emitPrototypeChecks):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::getByOffsetLoadElimination):
(JSC::DFG::CSEPhase::performNodeCSE):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGCommon.h:
(JSC::DFG::verboseCompilationEnabled):
(JSC::DFG::logCompilationChanges):
(JSC::DFG::shouldShowDisassembly):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
(JSC::DFG::ConstantFoldingPhase::emitGetByOffset):
* dfg/DFGDriver.cpp:
(JSC::DFG::compileImpl):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::convertToConstant):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToGetByOffset):
(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::hasMultiGetByOffsetData):
(JSC::DFG::Node::multiGetByOffsetData):
* dfg/DFGNodeType.h:
* dfg/DFGPhase.h:
(JSC::DFG::Phase::graph):
(JSC::DFG::runAndLog):
* dfg/DFGPlan.cpp:
(JSC::DFG::dumpAndVerifyGraph):
(JSC::DFG::Plan::compileInThread):
(JSC::DFG::Plan::compileInThreadImpl):
* 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):
* dfg/DFGTypeCheckHoistingPhase.cpp:
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLCompile.cpp:
(JSC::FTL::fixFunctionBasedOnStackMaps):
(JSC::FTL::compile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileMultiGetByOffset):
* ftl/FTLState.h:
(JSC::FTL::verboseCompilationEnabled):
(JSC::FTL::showDisassembly):
* jsc.cpp:
(GlobalObject::finishCreation):
(functionEffectful42):
* runtime/IntendedStructureChain.cpp:
(JSC::IntendedStructureChain::dump):
(JSC::IntendedStructureChain::dumpInContext):
* runtime/IntendedStructureChain.h:
* runtime/Options.cpp:
(JSC::recomputeDependentOptions):
* runtime/Options.h:
* tests/stress/fold-multi-get-by-offset-to-get-by-offset-with-watchpoint.js: Added.
(foo):
(bar):
* tests/stress/fold-multi-get-by-offset-to-get-by-offset.js: Added.
(foo):
(bar):
* tests/stress/multi-get-by-offset-proto-and-self.js: Added.
(foo):
(Foo):
Source/WTF:
Reviewed by Oliver Hunt.
* wtf/PrintStream.h:
(WTF::PointerDumpInContext::PointerDumpInContext):
(WTF::PointerDumpInContext::dump):
(WTF::pointerDumpInContext):
LayoutTests:
Reviewed by Oliver Hunt.
* js/regress/polymorphic-get-by-id-expected.txt: Added.
* js/regress/polymorphic-get-by-id.html: Added.
* js/regress/script-tests/polymorphic-get-by-id.js: Added.
(foo):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@164207 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index 1db2094..cf35c76 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -70,7 +70,9 @@
bytecode/DeferredCompilationCallback.cpp
bytecode/ExecutionCounter.cpp
bytecode/ExitKind.cpp
+ bytecode/ExitingJITType.cpp
bytecode/GetByIdStatus.cpp
+ bytecode/GetByIdVariant.cpp
bytecode/InlineCallFrameSet.cpp
bytecode/JumpTable.cpp
bytecode/LazyOperandValueProfile.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 1fbe6ec..61dd10b 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,129 @@
+2014-02-15 Filip Pizlo <fpizlo@apple.com>
+
+ FTL should inline polymorphic heap accesses
+ https://bugs.webkit.org/show_bug.cgi?id=128795
+
+ Reviewed by Oliver Hunt.
+
+ We now inline GetByIds that we know are pure but polymorphic. They manifest in DFG IR
+ as MultiGetByOffset, and in LLVM IR as a switch with a basic block for each kind of
+ read.
+
+ 2% speed-up on Octane mostly due to a 18% speed-up on deltablue.
+
+ * CMakeLists.txt:
+ * GNUmakefile.list.am:
+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dumpBytecode):
+ * bytecode/ExitingJITType.cpp: Added.
+ (WTF::printInternal):
+ * bytecode/ExitingJITType.h:
+ * bytecode/GetByIdStatus.cpp:
+ (JSC::GetByIdStatus::computeFromLLInt):
+ (JSC::GetByIdStatus::computeForChain):
+ (JSC::GetByIdStatus::computeForStubInfo):
+ (JSC::GetByIdStatus::computeFor):
+ (JSC::GetByIdStatus::dump):
+ * bytecode/GetByIdStatus.h:
+ (JSC::GetByIdStatus::GetByIdStatus):
+ (JSC::GetByIdStatus::numVariants):
+ (JSC::GetByIdStatus::variants):
+ (JSC::GetByIdStatus::at):
+ (JSC::GetByIdStatus::operator[]):
+ * bytecode/GetByIdVariant.cpp: Added.
+ (JSC::GetByIdVariant::dump):
+ (JSC::GetByIdVariant::dumpInContext):
+ * bytecode/GetByIdVariant.h: Added.
+ (JSC::GetByIdVariant::GetByIdVariant):
+ (JSC::GetByIdVariant::isSet):
+ (JSC::GetByIdVariant::operator!):
+ (JSC::GetByIdVariant::structureSet):
+ (JSC::GetByIdVariant::chain):
+ (JSC::GetByIdVariant::specificValue):
+ (JSC::GetByIdVariant::offset):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::emitPrototypeChecks):
+ (JSC::DFG::ByteCodeParser::handleGetById):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGCSEPhase.cpp:
+ (JSC::DFG::CSEPhase::getByOffsetLoadElimination):
+ (JSC::DFG::CSEPhase::performNodeCSE):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGCommon.h:
+ (JSC::DFG::verboseCompilationEnabled):
+ (JSC::DFG::logCompilationChanges):
+ (JSC::DFG::shouldShowDisassembly):
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ (JSC::DFG::ConstantFoldingPhase::emitGetByOffset):
+ * dfg/DFGDriver.cpp:
+ (JSC::DFG::compileImpl):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::Graph::dump):
+ * dfg/DFGGraph.h:
+ (JSC::DFG::Graph::convertToConstant):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::convertToGetByOffset):
+ (JSC::DFG::Node::hasHeapPrediction):
+ (JSC::DFG::Node::hasMultiGetByOffsetData):
+ (JSC::DFG::Node::multiGetByOffsetData):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPhase.h:
+ (JSC::DFG::Phase::graph):
+ (JSC::DFG::runAndLog):
+ * dfg/DFGPlan.cpp:
+ (JSC::DFG::dumpAndVerifyGraph):
+ (JSC::DFG::Plan::compileInThread):
+ (JSC::DFG::Plan::compileInThreadImpl):
+ * 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):
+ * dfg/DFGTypeCheckHoistingPhase.cpp:
+ (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
+ (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLCompile.cpp:
+ (JSC::FTL::fixFunctionBasedOnStackMaps):
+ (JSC::FTL::compile):
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::LowerDFGToLLVM::compileNode):
+ (JSC::FTL::LowerDFGToLLVM::compileMultiGetByOffset):
+ * ftl/FTLState.h:
+ (JSC::FTL::verboseCompilationEnabled):
+ (JSC::FTL::showDisassembly):
+ * jsc.cpp:
+ (GlobalObject::finishCreation):
+ (functionEffectful42):
+ * runtime/IntendedStructureChain.cpp:
+ (JSC::IntendedStructureChain::dump):
+ (JSC::IntendedStructureChain::dumpInContext):
+ * runtime/IntendedStructureChain.h:
+ * runtime/Options.cpp:
+ (JSC::recomputeDependentOptions):
+ * runtime/Options.h:
+ * tests/stress/fold-multi-get-by-offset-to-get-by-offset-with-watchpoint.js: Added.
+ (foo):
+ (bar):
+ * tests/stress/fold-multi-get-by-offset-to-get-by-offset.js: Added.
+ (foo):
+ (bar):
+ * tests/stress/multi-get-by-offset-proto-and-self.js: Added.
+ (foo):
+ (Foo):
+
2014-02-16 Filip Pizlo <fpizlo@apple.com>
DFG::prepareOSREntry should be nice to the stack
diff --git a/Source/JavaScriptCore/GNUmakefile.list.am b/Source/JavaScriptCore/GNUmakefile.list.am
index 5682d82..60049b8 100644
--- a/Source/JavaScriptCore/GNUmakefile.list.am
+++ b/Source/JavaScriptCore/GNUmakefile.list.am
@@ -147,10 +147,14 @@
Source/JavaScriptCore/bytecode/ExecutionCounter.h \
Source/JavaScriptCore/bytecode/ExitKind.cpp \
Source/JavaScriptCore/bytecode/ExitKind.h \
+ Source/JavaScriptCore/bytecode/ExitingJITType.cpp \
+ Source/JavaScriptCore/bytecode/ExitingJITType.h \
Source/JavaScriptCore/bytecode/ExpressionRangeInfo.h \
Source/JavaScriptCore/bytecode/FullBytecodeLiveness.h \
Source/JavaScriptCore/bytecode/GetByIdStatus.cpp \
Source/JavaScriptCore/bytecode/GetByIdStatus.h \
+ Source/JavaScriptCore/bytecode/GetByIdVariant.cpp \
+ Source/JavaScriptCore/bytecode/GetByIdVariant.h \
Source/JavaScriptCore/bytecode/HandlerInfo.h \
Source/JavaScriptCore/bytecode/InlineCallFrameSet.cpp \
Source/JavaScriptCore/bytecode/InlineCallFrameSet.h \
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
index 7954ec2..023cfad 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
@@ -325,7 +325,9 @@
<ClCompile Include="..\bytecode\DFGExitProfile.cpp" />
<ClCompile Include="..\bytecode\ExecutionCounter.cpp" />
<ClCompile Include="..\bytecode\ExitKind.cpp" />
+ <ClCompile Include="..\bytecode\ExitingJITType.cpp" />
<ClCompile Include="..\bytecode\GetByIdStatus.cpp" />
+ <ClCompile Include="..\bytecode\GetByIdVariant.cpp" />
<ClCompile Include="..\bytecode\InlineCallFrameSet.cpp" />
<ClCompile Include="..\bytecode\JumpTable.cpp" />
<ClCompile Include="..\bytecode\LazyOperandValueProfile.cpp" />
@@ -840,6 +842,7 @@
<ClInclude Include="..\bytecode\ExitKind.h" />
<ClInclude Include="..\bytecode\ExpressionRangeInfo.h" />
<ClInclude Include="..\bytecode\GetByIdStatus.h" />
+ <ClInclude Include="..\bytecode\GetByIdVariant.h" />
<ClInclude Include="..\bytecode\HandlerInfo.h" />
<ClInclude Include="..\bytecode\InlineCallFrameSet.h" />
<ClInclude Include="..\bytecode\Instruction.h" />
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index 9bdc910..dded8a3 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -59,6 +59,9 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
+ 0F0332C018ADFAE1005F979A /* ExitingJITType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F0332BF18ADFAE1005F979A /* ExitingJITType.cpp */; };
+ 0F0332C318B01763005F979A /* GetByIdVariant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F0332C118B01763005F979A /* GetByIdVariant.cpp */; };
+ 0F0332C418B01763005F979A /* GetByIdVariant.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0332C218B01763005F979A /* GetByIdVariant.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F05C3B41683CF9200BAF45B /* DFGArrayifySlowPathGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F05C3B21683CF8F00BAF45B /* DFGArrayifySlowPathGenerator.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F0776BF14FF002B00102332 /* JITCompilationEffort.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0776BD14FF002800102332 /* JITCompilationEffort.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F0B839C14BCF46300885B4F /* LLIntThunks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F0B839714BCF45A00885B4F /* LLIntThunks.cpp */; };
@@ -210,9 +213,9 @@
0F2FCCFD18A60070001A27F8 /* DFGScannable.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2FCCF618A60070001A27F8 /* DFGScannable.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F2FCCFE18A60070001A27F8 /* DFGThreadData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F2FCCF718A60070001A27F8 /* DFGThreadData.cpp */; };
0F2FCCFF18A60070001A27F8 /* DFGThreadData.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2FCCF818A60070001A27F8 /* DFGThreadData.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 0F300B7818AB051100A6D72E /* DFGNodeOrigin.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F300B7718AB051100A6D72E /* DFGNodeOrigin.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F300B7B18AB1B1400A6D72E /* DFGIntegerCheckCombiningPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F300B7918AB1B1400A6D72E /* DFGIntegerCheckCombiningPhase.cpp */; };
0F300B7C18AB1B1400A6D72E /* DFGIntegerCheckCombiningPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F300B7A18AB1B1400A6D72E /* DFGIntegerCheckCombiningPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
- 0F300B7818AB051100A6D72E /* DFGNodeOrigin.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F300B7718AB051100A6D72E /* DFGNodeOrigin.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F34B14916D42010001CDA5A /* DFGUseKind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F34B14716D4200E001CDA5A /* DFGUseKind.cpp */; };
0F34B14A16D42013001CDA5A /* DFGUseKind.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F34B14816D4200E001CDA5A /* DFGUseKind.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F34B14C16D43E0D001CDA5A /* PolymorphicAccessStructureList.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F34B14B16D43E0C001CDA5A /* PolymorphicAccessStructureList.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -1524,6 +1527,9 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 0F0332BF18ADFAE1005F979A /* ExitingJITType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExitingJITType.cpp; sourceTree = "<group>"; };
+ 0F0332C118B01763005F979A /* GetByIdVariant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GetByIdVariant.cpp; sourceTree = "<group>"; };
+ 0F0332C218B01763005F979A /* GetByIdVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetByIdVariant.h; sourceTree = "<group>"; };
0F05C3B21683CF8F00BAF45B /* DFGArrayifySlowPathGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGArrayifySlowPathGenerator.h; path = dfg/DFGArrayifySlowPathGenerator.h; sourceTree = "<group>"; };
0F0776BD14FF002800102332 /* JITCompilationEffort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITCompilationEffort.h; sourceTree = "<group>"; };
0F0B839714BCF45A00885B4F /* LLIntThunks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LLIntThunks.cpp; path = llint/LLIntThunks.cpp; sourceTree = "<group>"; };
@@ -1674,9 +1680,9 @@
0F2FCCF618A60070001A27F8 /* DFGScannable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGScannable.h; path = dfg/DFGScannable.h; sourceTree = "<group>"; };
0F2FCCF718A60070001A27F8 /* DFGThreadData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGThreadData.cpp; path = dfg/DFGThreadData.cpp; sourceTree = "<group>"; };
0F2FCCF818A60070001A27F8 /* DFGThreadData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGThreadData.h; path = dfg/DFGThreadData.h; sourceTree = "<group>"; };
+ 0F300B7718AB051100A6D72E /* DFGNodeOrigin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGNodeOrigin.h; path = dfg/DFGNodeOrigin.h; sourceTree = "<group>"; };
0F300B7918AB1B1400A6D72E /* DFGIntegerCheckCombiningPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGIntegerCheckCombiningPhase.cpp; path = dfg/DFGIntegerCheckCombiningPhase.cpp; sourceTree = "<group>"; };
0F300B7A18AB1B1400A6D72E /* DFGIntegerCheckCombiningPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGIntegerCheckCombiningPhase.h; path = dfg/DFGIntegerCheckCombiningPhase.h; sourceTree = "<group>"; };
- 0F300B7718AB051100A6D72E /* DFGNodeOrigin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGNodeOrigin.h; path = dfg/DFGNodeOrigin.h; sourceTree = "<group>"; };
0F34B14716D4200E001CDA5A /* DFGUseKind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGUseKind.cpp; path = dfg/DFGUseKind.cpp; sourceTree = "<group>"; };
0F34B14816D4200E001CDA5A /* DFGUseKind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGUseKind.h; path = dfg/DFGUseKind.h; sourceTree = "<group>"; };
0F34B14B16D43E0C001CDA5A /* PolymorphicAccessStructureList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PolymorphicAccessStructureList.h; sourceTree = "<group>"; };
@@ -4519,6 +4525,7 @@
969A07920ED1D3AE00F1F681 /* EvalCodeCache.h */,
0F56A1D415001CF2002992B1 /* ExecutionCounter.cpp */,
0F56A1D115000F31002992B1 /* ExecutionCounter.h */,
+ 0F0332BF18ADFAE1005F979A /* ExitingJITType.cpp */,
0F3AC753188E5EC80032029F /* ExitingJITType.h */,
0FB105821675480C00F8AB6E /* ExitKind.cpp */,
0FB105831675480C00F8AB6E /* ExitKind.h */,
@@ -4526,6 +4533,8 @@
0F666EBF183566F900D017F1 /* FullBytecodeLiveness.h */,
0F93329514CA7DC10085F3C6 /* GetByIdStatus.cpp */,
0F93329614CA7DC10085F3C6 /* GetByIdStatus.h */,
+ 0F0332C118B01763005F979A /* GetByIdVariant.cpp */,
+ 0F0332C218B01763005F979A /* GetByIdVariant.h */,
0F0B83A814BCF55E00885B4F /* HandlerInfo.h */,
0F24E55317F0B71C00ABB217 /* InlineCallFrameSet.cpp */,
0F24E55417F0B71C00ABB217 /* InlineCallFrameSet.h */,
@@ -4822,6 +4831,7 @@
0F63945515D07057006A597C /* ArrayProfile.h in Headers */,
BC18C3E70E16F5CD00B34460 /* ArrayPrototype.h in Headers */,
BC18C5240E16FC8A00B34460 /* ArrayPrototype.lut.h in Headers */,
+ 0F0332C418B01763005F979A /* GetByIdVariant.h in Headers */,
0FB7F39615ED8E4600F167B2 /* ArrayStorage.h in Headers */,
9688CB150ED12B4E001D649F /* AssemblerBuffer.h in Headers */,
86D3B2C510156BDE002865E7 /* AssemblerBufferWithConstantPool.h in Headers */,
@@ -6093,6 +6103,7 @@
86EC9DC71328DF82002B2AD7 /* DFGGraph.cpp in Sources */,
A704D90517A0BAA8006BA554 /* DFGInPlaceAbstractState.cpp in Sources */,
0FC97F3D18202119002C9B26 /* DFGInvalidationPointInjectionPhase.cpp in Sources */,
+ 0F0332C018ADFAE1005F979A /* ExitingJITType.cpp in Sources */,
0FEA0A33170D40BF00BB722C /* DFGJITCode.cpp in Sources */,
86EC9DCB1328DF82002B2AD7 /* DFGJITCompiler.cpp in Sources */,
A78A9778179738B8009DF744 /* DFGJITFinalizer.cpp in Sources */,
@@ -6376,6 +6387,7 @@
A513E5CA185F9624007E95AD /* InjectedScriptManager.cpp in Sources */,
148F21B7107EC5470042EC2C /* Nodes.cpp in Sources */,
655EB29B10CE2581001A990E /* NodesCodegen.cpp in Sources */,
+ 0F0332C318B01763005F979A /* GetByIdVariant.cpp in Sources */,
14469DE2107EC7E700650446 /* NumberConstructor.cpp in Sources */,
14469DE3107EC7E700650446 /* NumberObject.cpp in Sources */,
14469DE4107EC7E700650446 /* NumberPrototype.cpp in Sources */,
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index b576cd4..cdd668a 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -1377,7 +1377,7 @@
out.print(" !! frequent exits: ");
CommaPrinter comma;
for (unsigned i = 0; i < exitSites.size(); ++i)
- out.print(comma, exitSites[i].kind());
+ out.print(comma, exitSites[i].kind(), " ", exitSites[i].jitType());
}
#else // ENABLE(DFG_JIT)
UNUSED_PARAM(location);
diff --git a/Source/JavaScriptCore/bytecode/ExitingJITType.cpp b/Source/JavaScriptCore/bytecode/ExitingJITType.cpp
new file mode 100644
index 0000000..aa8f120
--- /dev/null
+++ b/Source/JavaScriptCore/bytecode/ExitingJITType.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * 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 "ExitingJITType.h"
+
+#include <wtf/PrintStream.h>
+
+namespace WTF {
+
+using namespace JSC;
+
+void printInternal(PrintStream& out, ExitingJITType type)
+{
+ switch (type) {
+ case ExitFromAnything:
+ out.print("FromAnything");
+ return;
+ case ExitFromDFG:
+ out.print("FromDFG");
+ return;
+ case ExitFromFTL:
+ out.print("FromFTL");
+ return;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WTF
+
diff --git a/Source/JavaScriptCore/bytecode/ExitingJITType.h b/Source/JavaScriptCore/bytecode/ExitingJITType.h
index fbdc28e..e8ed03e 100644
--- a/Source/JavaScriptCore/bytecode/ExitingJITType.h
+++ b/Source/JavaScriptCore/bytecode/ExitingJITType.h
@@ -51,5 +51,12 @@
} // namespace JSC
+namespace WTF {
+
+class PrintStream;
+void printInternal(PrintStream&, JSC::ExitingJITType);
+
+} // namespace WTF
+
#endif // ExitingJITType_h
diff --git a/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp b/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
index b8889d7..6e4e802 100644
--- a/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
+++ b/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
@@ -31,6 +31,7 @@
#include "LLIntData.h"
#include "LowLevelInterpreter.h"
#include "JSCInlines.h"
+#include <wtf/ListDump.h>
namespace JSC {
@@ -71,15 +72,17 @@
if (!isValidOffset(offset))
return GetByIdStatus(NoInformation, false);
- return GetByIdStatus(Simple, false, StructureSet(structure), offset, specificValue);
+ return GetByIdStatus(Simple, false, GetByIdVariant(StructureSet(structure), offset, specificValue));
#else
return GetByIdStatus(NoInformation, false);
#endif
}
-void GetByIdStatus::computeForChain(GetByIdStatus& result, CodeBlock* profiledBlock, StringImpl* uid)
+bool GetByIdStatus::computeForChain(CodeBlock* profiledBlock, StringImpl* uid, PassRefPtr<IntendedStructureChain> passedChain)
{
#if ENABLE(JIT)
+ RefPtr<IntendedStructureChain> chain = passedChain;
+
// Validate the chain. If the chain is invalid, then currently the best thing
// we can do is to assume that TakesSlow is true. In the future, it might be
// worth exploring reifying the structure chain from the structure we've got
@@ -90,40 +93,42 @@
// have now is that if the structure chain has changed between when it was
// cached on in the baseline JIT and when the DFG tried to inline the access,
// then we fall back on a polymorphic access.
- if (!result.m_chain->isStillValid())
- return;
+ if (!chain->isStillValid())
+ return false;
- if (result.m_chain->head()->takesSlowPathInDFGForImpureProperty())
- return;
- size_t chainSize = result.m_chain->size();
+ if (chain->head()->takesSlowPathInDFGForImpureProperty())
+ return false;
+ size_t chainSize = chain->size();
for (size_t i = 0; i < chainSize; i++) {
- if (result.m_chain->at(i)->takesSlowPathInDFGForImpureProperty())
- return;
+ if (chain->at(i)->takesSlowPathInDFGForImpureProperty())
+ return false;
}
- JSObject* currentObject = result.m_chain->terminalPrototype();
- Structure* currentStructure = result.m_chain->last();
+ JSObject* currentObject = chain->terminalPrototype();
+ Structure* currentStructure = chain->last();
ASSERT_UNUSED(currentObject, currentObject);
-
+
unsigned attributesIgnored;
JSCell* specificValue;
-
- result.m_offset = currentStructure->getConcurrently(
+
+ PropertyOffset offset = currentStructure->getConcurrently(
*profiledBlock->vm(), uid, attributesIgnored, specificValue);
if (currentStructure->isDictionary())
specificValue = 0;
- if (!isValidOffset(result.m_offset))
- return;
-
- result.m_structureSet.add(result.m_chain->head());
- result.m_specificValue = JSValue(specificValue);
-#else
- UNUSED_PARAM(result);
+ if (!isValidOffset(offset))
+ return false;
+
+ m_variants.append(
+ GetByIdVariant(StructureSet(chain->head()), offset, specificValue, chain));
+ return true;
+#else // ENABLE(JIT)
UNUSED_PARAM(profiledBlock);
UNUSED_PARAM(uid);
+ UNUSED_PARAM(passedChain);
UNREACHABLE_FOR_PLATFORM();
-#endif
+ return false;
+#endif // ENABLE(JIT)
}
GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, StringImpl* uid)
@@ -174,6 +179,7 @@
// Finally figure out if we can derive an access strategy.
GetByIdStatus result;
+ result.m_state = Simple;
result.m_wasSeenInJIT = true; // This is interesting for bytecode dumping only.
switch (stubInfo->accessType) {
case access_unset:
@@ -185,31 +191,37 @@
return GetByIdStatus(TakesSlowPath, true);
unsigned attributesIgnored;
JSCell* specificValue;
- result.m_offset = structure->getConcurrently(
+ GetByIdVariant variant;
+ variant.m_offset = structure->getConcurrently(
*profiledBlock->vm(), uid, attributesIgnored, specificValue);
+ if (!isValidOffset(variant.m_offset))
+ return GetByIdStatus(TakesSlowPath, true);
+
if (structure->isDictionary())
specificValue = 0;
- if (isValidOffset(result.m_offset)) {
- result.m_structureSet.add(structure);
- result.m_specificValue = JSValue(specificValue);
- }
-
- if (isValidOffset(result.m_offset))
- ASSERT(result.m_structureSet.size());
- break;
+ variant.m_structureSet.add(structure);
+ variant.m_specificValue = JSValue(specificValue);
+ result.m_variants.append(variant);
+ return result;
}
case access_get_by_id_self_list: {
- for (int i = 0; i < listSize; ++i) {
- ASSERT(list->list[i].isDirect);
+ for (int listIndex = 0; listIndex < listSize; ++listIndex) {
+ ASSERT(list->list[listIndex].isDirect);
- Structure* structure = list->list[i].base.get();
+ Structure* structure = list->list[listIndex].base.get();
if (structure->takesSlowPathInDFGForImpureProperty())
return GetByIdStatus(TakesSlowPath, true);
-
- if (result.m_structureSet.contains(structure))
+
+ if (list->list[listIndex].chain.get()) {
+ RefPtr<IntendedStructureChain> chain = adoptRef(new IntendedStructureChain(
+ profiledBlock, structure, list->list[listIndex].chain.get(),
+ list->list[listIndex].count));
+ if (!result.computeForChain(profiledBlock, uid, chain))
+ return GetByIdStatus(TakesSlowPath, true);
continue;
+ }
unsigned attributesIgnored;
JSCell* specificValue;
@@ -218,54 +230,58 @@
if (structure->isDictionary())
specificValue = 0;
- if (!isValidOffset(myOffset)) {
- result.m_offset = invalidOffset;
+ if (!isValidOffset(myOffset))
+ return GetByIdStatus(TakesSlowPath, true);
+
+ bool found = false;
+ for (unsigned variantIndex = 0; variantIndex < result.m_variants.size(); ++variantIndex) {
+ GetByIdVariant& variant = result.m_variants[variantIndex];
+ if (variant.m_chain)
+ continue;
+
+ if (variant.m_offset != myOffset)
+ continue;
+
+ found = true;
+ if (variant.m_structureSet.contains(structure))
+ break;
+
+ if (variant.m_specificValue != JSValue(specificValue))
+ variant.m_specificValue = JSValue();
+
+ variant.m_structureSet.add(structure);
break;
}
-
- if (!i) {
- result.m_offset = myOffset;
- result.m_specificValue = JSValue(specificValue);
- } else if (result.m_offset != myOffset) {
- result.m_offset = invalidOffset;
- break;
- } else if (result.m_specificValue != JSValue(specificValue))
- result.m_specificValue = JSValue();
- result.m_structureSet.add(structure);
+ if (found)
+ continue;
+
+ result.m_variants.append(
+ GetByIdVariant(StructureSet(structure), myOffset, specificValue));
}
-
- if (isValidOffset(result.m_offset))
- ASSERT(result.m_structureSet.size());
- break;
+
+ return result;
}
case access_get_by_id_chain: {
if (!stubInfo->u.getByIdChain.isDirect)
return GetByIdStatus(MakesCalls, true);
- result.m_chain = adoptRef(new IntendedStructureChain(
+ RefPtr<IntendedStructureChain> chain = adoptRef(new IntendedStructureChain(
profiledBlock,
stubInfo->u.getByIdChain.baseObjectStructure.get(),
stubInfo->u.getByIdChain.chain.get(),
stubInfo->u.getByIdChain.count));
- computeForChain(result, profiledBlock, uid);
- break;
+ if (result.computeForChain(profiledBlock, uid, chain))
+ return result;
+ return GetByIdStatus(TakesSlowPath, true);
}
default:
- ASSERT(!isValidOffset(result.m_offset));
- break;
+ return GetByIdStatus(TakesSlowPath, true);
}
- if (!isValidOffset(result.m_offset)) {
- result.m_state = TakesSlowPath;
- result.m_structureSet.clear();
- result.m_chain.clear();
- result.m_specificValue = JSValue();
- } else
- result.m_state = Simple;
-
- return result;
+ RELEASE_ASSERT_NOT_REACHED();
+ return GetByIdStatus();
}
#endif // ENABLE(JIT)
@@ -280,7 +296,7 @@
ConcurrentJITLocker locker(dfgBlock->m_lock);
result = computeForStubInfo(locker, dfgBlock, dfgMap.get(codeOrigin), uid);
}
-
+
if (result.takesSlowPath())
return result;
@@ -318,21 +334,37 @@
if (!structure->propertyAccessesAreCacheable())
return GetByIdStatus(TakesSlowPath);
- GetByIdStatus result;
- result.m_wasSeenInJIT = false; // To my knowledge nobody that uses computeFor(VM&, Structure*, StringImpl*) reads this field, but I might as well be honest: no, it wasn't seen in the JIT, since I computed it statically.
unsigned attributes;
JSCell* specificValue;
- result.m_offset = structure->getConcurrently(vm, uid, attributes, specificValue);
- if (!isValidOffset(result.m_offset))
+ PropertyOffset offset = structure->getConcurrently(vm, uid, attributes, specificValue);
+ if (!isValidOffset(offset))
return GetByIdStatus(TakesSlowPath); // It's probably a prototype lookup. Give up on life for now, even though we could totally be way smarter about it.
if (attributes & Accessor)
return GetByIdStatus(MakesCalls);
if (structure->isDictionary())
specificValue = 0;
- result.m_structureSet.add(structure);
- result.m_specificValue = JSValue(specificValue);
- result.m_state = Simple;
- return result;
+ return GetByIdStatus(
+ Simple, false, GetByIdVariant(StructureSet(structure), offset, specificValue));
+}
+
+void GetByIdStatus::dump(PrintStream& out) const
+{
+ out.print("(");
+ switch (m_state) {
+ case NoInformation:
+ out.print("NoInformation");
+ break;
+ case Simple:
+ out.print("Simple");
+ break;
+ case TakesSlowPath:
+ out.print("TakesSlowPath");
+ break;
+ case MakesCalls:
+ out.print("MakesCalls");
+ break;
+ }
+ out.print(", ", listDump(m_variants), ", seenInJIT = ", m_wasSeenInJIT, ")");
}
} // namespace JSC
diff --git a/Source/JavaScriptCore/bytecode/GetByIdStatus.h b/Source/JavaScriptCore/bytecode/GetByIdStatus.h
index 685fa1b..94f4dbb 100644
--- a/Source/JavaScriptCore/bytecode/GetByIdStatus.h
+++ b/Source/JavaScriptCore/bytecode/GetByIdStatus.h
@@ -29,9 +29,7 @@
#include "CodeOrigin.h"
#include "ConcurrentJITLock.h"
#include "ExitingJITType.h"
-#include "IntendedStructureChain.h"
-#include "PropertyOffset.h"
-#include "StructureSet.h"
+#include "GetByIdVariant.h"
#include "StructureStubInfo.h"
namespace JSC {
@@ -50,28 +48,22 @@
GetByIdStatus()
: m_state(NoInformation)
- , m_offset(invalidOffset)
{
}
explicit GetByIdStatus(State state)
: m_state(state)
- , m_offset(invalidOffset)
{
ASSERT(state == NoInformation || state == TakesSlowPath || state == MakesCalls);
}
GetByIdStatus(
- State state, bool wasSeenInJIT, const StructureSet& structureSet = StructureSet(),
- PropertyOffset offset = invalidOffset, JSValue specificValue = JSValue(), PassRefPtr<IntendedStructureChain> chain = nullptr)
+ State state, bool wasSeenInJIT, const GetByIdVariant& variant = GetByIdVariant())
: m_state(state)
- , m_structureSet(structureSet)
- , m_chain(chain)
- , m_specificValue(specificValue)
- , m_offset(offset)
, m_wasSeenInJIT(wasSeenInJIT)
{
- ASSERT((state == Simple) == (offset != invalidOffset));
+ ASSERT((state == Simple) == variant.isSet());
+ m_variants.append(variant);
}
static GetByIdStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex, StringImpl* uid);
@@ -84,16 +76,19 @@
bool isSet() const { return m_state != NoInformation; }
bool operator!() const { return !isSet(); }
bool isSimple() const { return m_state == Simple; }
+
+ size_t numVariants() const { return m_variants.size(); }
+ const Vector<GetByIdVariant, 1>& variants() const { return m_variants; }
+ const GetByIdVariant& at(size_t index) const { return m_variants[index]; }
+ const GetByIdVariant& operator[](size_t index) const { return at(index); }
+
bool takesSlowPath() const { return m_state == TakesSlowPath || m_state == MakesCalls; }
bool makesCalls() const { return m_state == MakesCalls; }
- const StructureSet& structureSet() const { return m_structureSet; }
- IntendedStructureChain* chain() const { return const_cast<IntendedStructureChain*>(m_chain.get()); } // Returns null if this is a direct access.
- JSValue specificValue() const { return m_specificValue; } // Returns JSValue() if there is no specific value.
- PropertyOffset offset() const { return m_offset; }
-
bool wasSeenInJIT() const { return m_wasSeenInJIT; }
+ void dump(PrintStream&) const;
+
private:
#if ENABLE(DFG_JIT)
static bool hasExitSite(const ConcurrentJITLocker&, CodeBlock*, unsigned bytecodeIndex, ExitingJITType = ExitFromAnything);
@@ -101,14 +96,11 @@
#if ENABLE(JIT)
static GetByIdStatus computeForStubInfo(const ConcurrentJITLocker&, CodeBlock*, StructureStubInfo*, StringImpl* uid);
#endif
- static void computeForChain(GetByIdStatus& result, CodeBlock*, StringImpl* uid);
+ bool computeForChain(CodeBlock*, StringImpl* uid, PassRefPtr<IntendedStructureChain>);
static GetByIdStatus computeFromLLInt(CodeBlock*, unsigned bytecodeIndex, StringImpl* uid);
State m_state;
- StructureSet m_structureSet;
- RefPtr<IntendedStructureChain> m_chain;
- JSValue m_specificValue;
- PropertyOffset m_offset;
+ Vector<GetByIdVariant, 1> m_variants;
bool m_wasSeenInJIT;
};
diff --git a/Source/JavaScriptCore/bytecode/GetByIdVariant.cpp b/Source/JavaScriptCore/bytecode/GetByIdVariant.cpp
new file mode 100644
index 0000000..b8bedce
--- /dev/null
+++ b/Source/JavaScriptCore/bytecode/GetByIdVariant.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * 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 "GetByIdVariant.h"
+
+#include "JSCInlines.h"
+
+namespace JSC {
+
+void GetByIdVariant::dump(PrintStream& out) const
+{
+ dumpInContext(out, 0);
+}
+
+void GetByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+ if (!isSet()) {
+ out.print("<empty>");
+ return;
+ }
+
+ out.print(
+ "<", inContext(structureSet(), context), ", ",
+ pointerDumpInContext(chain(), context), ", ",
+ inContext(specificValue(), context), ", ", offset(), ">");
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/GetByIdVariant.h b/Source/JavaScriptCore/bytecode/GetByIdVariant.h
new file mode 100644
index 0000000..f0201ee
--- /dev/null
+++ b/Source/JavaScriptCore/bytecode/GetByIdVariant.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * 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 GetByIdVariant_h
+#define GetByIdVariant_h
+
+#include "IntendedStructureChain.h"
+#include "JSCJSValue.h"
+#include "PropertyOffset.h"
+#include "StructureSet.h"
+
+namespace JSC {
+
+class GetByIdStatus;
+struct DumpContext;
+
+class GetByIdVariant {
+public:
+ GetByIdVariant(
+ const StructureSet& structureSet = StructureSet(),
+ PropertyOffset offset = invalidOffset, JSValue specificValue = JSValue(),
+ PassRefPtr<IntendedStructureChain> chain = nullptr)
+ : m_structureSet(structureSet)
+ , m_chain(chain)
+ , m_specificValue(specificValue)
+ , m_offset(offset)
+ {
+ if (!structureSet.size()) {
+ ASSERT(offset == invalidOffset);
+ ASSERT(!specificValue);
+ ASSERT(!chain);
+ }
+ }
+
+ bool isSet() const { return !!m_structureSet.size(); }
+ bool operator!() const { return !isSet(); }
+ const StructureSet& structureSet() const { return m_structureSet; }
+ IntendedStructureChain* chain() const { return const_cast<IntendedStructureChain*>(m_chain.get()); }
+ JSValue specificValue() const { return m_specificValue; }
+ PropertyOffset offset() const { return m_offset; }
+
+ void dump(PrintStream&) const;
+ void dumpInContext(PrintStream&, DumpContext*) const;
+
+private:
+ friend class GetByIdStatus;
+
+ StructureSet m_structureSet;
+ RefPtr<IntendedStructureChain> m_chain;
+ JSValue m_specificValue;
+ PropertyOffset m_offset;
+};
+
+} // namespace JSC
+
+#endif // GetByIdVariant_h
+
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index 7035890..df93d07 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -1451,17 +1451,17 @@
GetByIdStatus status = GetByIdStatus::computeFor(
m_graph.m_vm, structure,
m_graph.identifiers()[node->identifierNumber()]);
- if (status.isSimple()) {
+ if (status.isSimple() && status.numVariants() == 1) {
// Assert things that we can't handle and that the computeFor() method
// above won't be able to return.
- ASSERT(status.structureSet().size() == 1);
- ASSERT(!status.chain());
+ ASSERT(status[0].structureSet().size() == 1);
+ ASSERT(!status[0].chain());
- if (status.specificValue())
- setConstant(node, status.specificValue());
+ if (status[0].specificValue())
+ setConstant(node, status[0].specificValue());
else
forNode(node).makeHeapTop();
- filter(node->child1(), status.structureSet());
+ filter(node->child1(), status[0].structureSet());
m_state.setFoundConstants(true);
m_state.setHaveStructures(true);
@@ -1632,6 +1632,39 @@
forNode(node).makeHeapTop();
break;
}
+
+ case MultiGetByOffset: {
+ AbstractValue& value = forNode(node->child1());
+ ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this.
+
+ if (Structure* structure = value.bestProvenStructure()) {
+ bool done = false;
+ for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;) {
+ const GetByIdVariant& variant = node->multiGetByOffsetData().variants[i];
+ if (!variant.structureSet().contains(structure))
+ continue;
+
+ if (variant.chain())
+ break;
+
+ filter(value, structure);
+ forNode(node).makeHeapTop();
+ m_state.setFoundConstants(true);
+ done = true;
+ break;
+ }
+ if (done)
+ break;
+ }
+
+ StructureSet set;
+ for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;)
+ set.addAll(node->multiGetByOffsetData().variants[i].structureSet());
+
+ filter(value, set);
+ forNode(node).makeHeapTop();
+ break;
+ }
case PutByOffset: {
break;
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 0254bb2..dbb6492 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -180,6 +180,7 @@
void handleGetById(
int destinationOperand, SpeculatedType, Node* base, unsigned identifierNumber,
const GetByIdStatus&);
+ Node* emitPrototypeChecks(const GetByIdVariant&);
Node* getScope(bool skipTop, unsigned skipCount);
@@ -1827,6 +1828,21 @@
return result;
}
+Node* ByteCodeParser::emitPrototypeChecks(const GetByIdVariant& variant)
+{
+ Node* base = 0;
+ m_graph.chains().addLazily(variant.chain());
+ Structure* currentStructure = variant.structureSet().singletonStructure();
+ JSObject* currentObject = 0;
+ for (unsigned i = 0; i < variant.chain()->size(); ++i) {
+ currentObject = asObject(currentStructure->prototypeForLookup(m_inlineStackTop->m_codeBlock));
+ currentStructure = variant.chain()->at(i);
+ base = cellConstantWithStructureCheck(currentObject, currentStructure);
+ }
+ RELEASE_ASSERT(base);
+ return base;
+}
+
void ByteCodeParser::handleGetById(
int destinationOperand, SpeculatedType prediction, Node* base, unsigned identifierNumber,
const GetByIdStatus& getByIdStatus)
@@ -1839,25 +1855,42 @@
return;
}
- ASSERT(getByIdStatus.structureSet().size());
+ if (getByIdStatus.numVariants() > 1) {
+ if (!isFTL(m_graph.m_plan.mode)) {
+ set(VirtualRegister(destinationOperand),
+ addToGraph(GetById, OpInfo(identifierNumber), OpInfo(prediction), base));
+ return;
+ }
+
+ // 1) Emit prototype structure checks for all chains. This could sort of maybe not be
+ // optimal, if there is some rarely executed case in the chain that requires a lot
+ // of checks and those checks are not watchpointable.
+ for (unsigned variantIndex = getByIdStatus.numVariants(); variantIndex--;) {
+ if (getByIdStatus[variantIndex].chain())
+ emitPrototypeChecks(getByIdStatus[variantIndex]);
+ }
+
+ // 2) Emit a MultiGetByOffset
+ MultiGetByOffsetData* data = m_graph.m_multiGetByOffsetData.add();
+ data->variants = getByIdStatus.variants();
+ data->identifierNumber = identifierNumber;
+ set(VirtualRegister(destinationOperand),
+ addToGraph(MultiGetByOffset, OpInfo(data), OpInfo(prediction), base));
+ return;
+ }
+
+ ASSERT(getByIdStatus.numVariants() == 1);
+ GetByIdVariant variant = getByIdStatus[0];
if (m_graph.compilation())
m_graph.compilation()->noticeInlinedGetById();
Node* originalBaseForBaselineJIT = base;
- addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(getByIdStatus.structureSet())), base);
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.structureSet())), base);
- if (getByIdStatus.chain()) {
- m_graph.chains().addLazily(getByIdStatus.chain());
- Structure* currentStructure = getByIdStatus.structureSet().singletonStructure();
- JSObject* currentObject = 0;
- for (unsigned i = 0; i < getByIdStatus.chain()->size(); ++i) {
- currentObject = asObject(currentStructure->prototypeForLookup(m_inlineStackTop->m_codeBlock));
- currentStructure = getByIdStatus.chain()->at(i);
- base = cellConstantWithStructureCheck(currentObject, currentStructure);
- }
- }
+ if (variant.chain())
+ base = emitPrototypeChecks(variant);
// Unless we want bugs like https://bugs.webkit.org/show_bug.cgi?id=88783, we need to
// ensure that the base of the original get_by_id is kept alive until we're done with
@@ -1865,18 +1898,18 @@
// on something other than the base following the CheckStructure on base, or if the
// access was compiled to a WeakJSConstant specific value, in which case we might not
// have any explicit use of the base at all.
- if (getByIdStatus.specificValue() || originalBaseForBaselineJIT != base)
+ if (variant.specificValue() || originalBaseForBaselineJIT != base)
addToGraph(Phantom, originalBaseForBaselineJIT);
- if (getByIdStatus.specificValue()) {
- ASSERT(getByIdStatus.specificValue().isCell());
+ if (variant.specificValue()) {
+ ASSERT(variant.specificValue().isCell());
- set(VirtualRegister(destinationOperand), cellConstant(getByIdStatus.specificValue().asCell()));
+ set(VirtualRegister(destinationOperand), cellConstant(variant.specificValue().asCell()));
return;
}
handleGetByOffset(
- destinationOperand, prediction, base, identifierNumber, getByIdStatus.offset());
+ destinationOperand, prediction, base, identifierNumber, variant.offset());
}
void ByteCodeParser::prepareToParseBlock()
@@ -3140,13 +3173,13 @@
case GlobalProperty:
case GlobalPropertyWithVarInjectionChecks: {
GetByIdStatus status = GetByIdStatus::computeFor(*m_vm, structure, uid);
- if (status.takesSlowPath()) {
+ if (status.state() != GetByIdStatus::Simple || status.numVariants() != 1) {
set(VirtualRegister(dst), addToGraph(GetByIdFlush, OpInfo(identifierNumber), OpInfo(prediction), get(VirtualRegister(scope))));
break;
}
- Node* base = cellConstantWithStructureCheck(globalObject, status.structureSet().singletonStructure());
+ Node* base = cellConstantWithStructureCheck(globalObject, status[0].structureSet().singletonStructure());
addToGraph(Phantom, get(VirtualRegister(scope)));
- if (JSValue specificValue = status.specificValue())
+ if (JSValue specificValue = status[0].specificValue())
set(VirtualRegister(dst), cellConstant(specificValue.asCell()));
else
set(VirtualRegister(dst), handleGetByOffset(prediction, base, identifierNumber, operand));
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index d6db78b..f410fa5 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -642,23 +642,29 @@
return 0;
}
- Node* getByOffsetLoadElimination(unsigned identifierNumber, Node* child1)
+ Node* getByOffsetLoadElimination(unsigned identifierNumber, Node* base)
{
for (unsigned i = m_indexInBlock; i--;) {
Node* node = m_currentBlock->at(i);
- if (node == child1)
+ if (node == base)
break;
switch (node->op()) {
case GetByOffset:
- if (node->child1() == child1
+ if (node->child2() == base
&& m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber == identifierNumber)
return node;
break;
+ case MultiGetByOffset:
+ if (node->child1() == base
+ && node->multiGetByOffsetData().identifierNumber == identifierNumber)
+ return node;
+ break;
+
case PutByOffset:
if (m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber == identifierNumber) {
- if (node->child1() == child1) // Must be same property storage.
+ if (node->child2() == base) // Must be same property storage.
return node->child3().node();
return 0;
}
@@ -1401,7 +1407,13 @@
case GetByOffset:
if (cseMode == StoreElimination)
break;
- setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber, node->child1().node()));
+ setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber, node->child2().node()));
+ break;
+
+ case MultiGetByOffset:
+ if (cseMode == StoreElimination)
+ break;
+ setReplacement(getByOffsetLoadElimination(node->multiGetByOffsetData().identifierNumber, node->child1().node()));
break;
case PutByOffset:
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index dc7d1eb..55db57d 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -470,6 +470,10 @@
read(AbstractHeap(NamedProperties, graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber));
return;
+ case MultiGetByOffset:
+ read(AbstractHeap(NamedProperties, node->multiGetByOffsetData().identifierNumber));
+ return;
+
case PutByOffset:
write(AbstractHeap(NamedProperties, graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber));
return;
diff --git a/Source/JavaScriptCore/dfg/DFGCommon.h b/Source/JavaScriptCore/dfg/DFGCommon.h
index e9eacbd..9c8a934 100644
--- a/Source/JavaScriptCore/dfg/DFGCommon.h
+++ b/Source/JavaScriptCore/dfg/DFGCommon.h
@@ -28,6 +28,8 @@
#include <wtf/Platform.h>
+#include "DFGCompilationMode.h"
+
#if ENABLE(DFG_JIT)
#include "CodeOrigin.h"
@@ -63,14 +65,14 @@
DontRefNode
};
-inline bool verboseCompilationEnabled()
+inline bool verboseCompilationEnabled(CompilationMode mode = DFGMode)
{
- return Options::verboseCompilation() || Options::dumpGraphAtEachPhase();
+ return Options::verboseCompilation() || Options::dumpGraphAtEachPhase() || (isFTL(mode) && Options::verboseFTLCompilation());
}
-inline bool logCompilationChanges()
+inline bool logCompilationChanges(CompilationMode mode = DFGMode)
{
- return verboseCompilationEnabled() || Options::logCompilationChanges();
+ return verboseCompilationEnabled(mode) || Options::logCompilationChanges();
}
inline bool shouldDumpGraphAtEachPhase()
@@ -288,10 +290,10 @@
}
// Unconditionally disable DFG disassembly support if the DFG is not compiled in.
-inline bool shouldShowDisassembly()
+inline bool shouldShowDisassembly(CompilationMode mode = DFGMode)
{
#if ENABLE(DFG_JIT)
- return Options::showDisassembly() || Options::showDFGDisassembly();
+ return Options::showDisassembly() || Options::showDFGDisassembly() || (isFTL(mode) && Options::showFTLDisassembly());
#else
return false;
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
index d4d7330..a8056cb 100644
--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
@@ -151,10 +151,33 @@
break;
}
+
+ case MultiGetByOffset: {
+ Edge childEdge = node->child1();
+ Node* child = childEdge.node();
+ MultiGetByOffsetData& data = node->multiGetByOffsetData();
+
+ Structure* structure = m_state.forNode(child).bestProvenStructure();
+ if (!structure)
+ break;
+
+ for (unsigned i = data.variants.size(); i--;) {
+ const GetByIdVariant& variant = data.variants[i];
+ if (!variant.structureSet().contains(structure))
+ continue;
+
+ if (variant.chain())
+ break;
+
+ emitGetByOffset(indexInBlock, node, structure, variant, data.identifierNumber);
+ eliminated = true;
+ break;
+ }
+ break;
+ }
case GetById:
case GetByIdFlush: {
- NodeOrigin origin = node->origin;
Edge childEdge = node->child1();
Node* child = childEdge.node();
unsigned identifierNumber = node->identifierNumber();
@@ -166,54 +189,17 @@
if (!structure)
break;
- bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
- bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
-
GetByIdStatus status = GetByIdStatus::computeFor(
vm(), structure, m_graph.identifiers()[identifierNumber]);
- if (!status.isSimple()) {
+ if (!status.isSimple() || status.numVariants() != 1) {
// FIXME: We could handle prototype cases.
// https://bugs.webkit.org/show_bug.cgi?id=110386
break;
}
- ASSERT(status.structureSet().size() == 1);
- ASSERT(!status.chain());
- ASSERT(status.structureSet().singletonStructure() == structure);
-
- // Now before we do anything else, push the CFA forward over the GetById
- // and make sure we signal to the loop that it should continue and not
- // do any eliminations.
- m_interpreter.execute(indexInBlock);
+ emitGetByOffset(indexInBlock, node, structure, status[0], identifierNumber);
eliminated = true;
-
- if (needsWatchpoint) {
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, StructureTransitionWatchpoint, origin,
- OpInfo(structure), childEdge);
- } else if (needsCellCheck) {
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, Phantom, origin, childEdge);
- }
-
- childEdge.setUseKind(KnownCellUse);
-
- Edge propertyStorage;
-
- if (isInlineOffset(status.offset()))
- propertyStorage = childEdge;
- else {
- propertyStorage = Edge(m_insertionSet.insertNode(
- indexInBlock, SpecNone, GetButterfly, origin, childEdge));
- }
-
- node->convertToGetByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
-
- StorageAccessData storageAccessData;
- storageAccessData.offset = status.offset();
- storageAccessData.identifierNumber = identifierNumber;
- m_graph.m_storageAccessData.append(storageAccessData);
break;
}
@@ -406,6 +392,56 @@
return changed;
}
+
+ void emitGetByOffset(unsigned indexInBlock, Node* node, Structure* structure, const GetByIdVariant& variant, unsigned identifierNumber)
+ {
+ NodeOrigin origin = node->origin;
+ Edge childEdge = node->child1();
+ Node* child = childEdge.node();
+
+ bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
+ bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
+
+ ASSERT(!variant.chain());
+ ASSERT(variant.structureSet().contains(structure));
+
+ // Now before we do anything else, push the CFA forward over the GetById
+ // and make sure we signal to the loop that it should continue and not
+ // do any eliminations.
+ m_interpreter.execute(indexInBlock);
+
+ if (needsWatchpoint) {
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, StructureTransitionWatchpoint, origin,
+ OpInfo(structure), childEdge);
+ } else if (needsCellCheck) {
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, Phantom, origin, childEdge);
+ }
+
+ if (variant.specificValue()) {
+ m_graph.convertToConstant(node, variant.specificValue());
+ return;
+ }
+
+ childEdge.setUseKind(KnownCellUse);
+
+ Edge propertyStorage;
+
+ if (isInlineOffset(variant.offset()))
+ propertyStorage = childEdge;
+ else {
+ propertyStorage = Edge(m_insertionSet.insertNode(
+ indexInBlock, SpecNone, GetButterfly, origin, childEdge));
+ }
+
+ node->convertToGetByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
+
+ StorageAccessData storageAccessData;
+ storageAccessData.offset = variant.offset();
+ storageAccessData.identifierNumber = identifierNumber;
+ m_graph.m_storageAccessData.append(storageAccessData);
+ }
void addStructureTransitionCheck(NodeOrigin origin, unsigned indexInBlock, JSCell* cell)
{
diff --git a/Source/JavaScriptCore/dfg/DFGDriver.cpp b/Source/JavaScriptCore/dfg/DFGDriver.cpp
index b65e515..b29df5f 100644
--- a/Source/JavaScriptCore/dfg/DFGDriver.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDriver.cpp
@@ -82,7 +82,7 @@
if (debugger && (debugger->isStepping() || codeBlock->baselineAlternative()->hasDebuggerRequests()))
return CompilationInvalidated;
- if (logCompilationChanges())
+ if (logCompilationChanges(mode))
dataLog("DFG(Driver) compiling ", *codeBlock, " with ", mode, ", number of instructions = ", codeBlock->instructionCount(), "\n");
// Make sure that any stubs that the DFG is going to use are initialized. We want to
@@ -115,7 +115,7 @@
if (enableConcurrentJIT) {
Worklist* worklist = ensureGlobalWorklistFor(mode);
plan->callback = callback;
- if (logCompilationChanges())
+ if (logCompilationChanges(mode))
dataLog("Deferring DFG compilation of ", *codeBlock, " with queue length ", worklist->queueLength(), ".\n");
worklist->enqueue(plan);
return CompilationDeferred;
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 33c7cef..0ab2b37 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -881,6 +881,11 @@
break;
}
+ case MultiGetByOffset: {
+ fixEdge<CellUse>(node->child1());
+ break;
+ }
+
case PutByOffset: {
if (!node->child1()->hasStorageResult())
fixEdge<KnownCellUse>(node->child1());
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp
index 5fe44ad..df49ece 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.cpp
+++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp
@@ -246,6 +246,12 @@
out.print(comma, "id", storageAccessData.identifierNumber, "{", identifiers()[storageAccessData.identifierNumber], "}");
out.print(", ", static_cast<ptrdiff_t>(storageAccessData.offset));
}
+ if (node->hasMultiGetByOffsetData()) {
+ MultiGetByOffsetData& data = node->multiGetByOffsetData();
+ out.print(comma, "id", data.identifierNumber, "{", identifiers()[data.identifierNumber], "}");
+ for (unsigned i = 0; i < data.variants.size(); ++i)
+ out.print(comma, inContext(data.variants[i], context));
+ }
ASSERT(node->hasVariableAccessData(*this) == node->hasLocal(*this));
if (node->hasVariableAccessData(*this)) {
VariableAccessData* variableAccessData = node->tryGetVariableAccessData();
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h
index 1631ecc..8725d14 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.h
+++ b/Source/JavaScriptCore/dfg/DFGGraph.h
@@ -177,7 +177,10 @@
void convertToConstant(Node* node, JSValue value)
{
- convertToConstant(node, constantRegisterForConstant(value));
+ if (value.isObject())
+ node->convertToWeakConstant(value.asCell());
+ else
+ convertToConstant(node, constantRegisterForConstant(value));
}
// CodeBlock is optional, but may allow additional information to be dumped (e.g. Identifier names).
@@ -827,6 +830,7 @@
SegmentedVector<StructureTransitionData, 8> m_structureTransitionData;
SegmentedVector<NewArrayBufferData, 4> m_newArrayBufferData;
SegmentedVector<SwitchData, 4> m_switchData;
+ Bag<MultiGetByOffsetData> m_multiGetByOffsetData;
Vector<InlineVariableData, 4> m_inlineVariableData;
OwnPtr<InlineCallFrameSet> m_inlineCallFrames;
HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>> m_bytecodeLiveness;
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index beddaed..d998600 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -41,6 +41,7 @@
#include "DFGNodeOrigin.h"
#include "DFGNodeType.h"
#include "DFGVariableAccessData.h"
+#include "GetByIdVariant.h"
#include "JSCJSValue.h"
#include "Operands.h"
#include "SpeculatedType.h"
@@ -53,6 +54,11 @@
class Graph;
struct BasicBlock;
+struct MultiGetByOffsetData {
+ unsigned identifierNumber;
+ Vector<GetByIdVariant, 2> variants;
+};
+
struct StructureTransitionData {
Structure* previousStructure;
Structure* newStructure;
@@ -406,7 +412,7 @@
void convertToGetByOffset(unsigned storageAccessDataIndex, Edge storage)
{
- ASSERT(m_op == GetById || m_op == GetByIdFlush);
+ ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == MultiGetByOffset);
m_opInfo = storageAccessDataIndex;
children.setChild2(children.child1());
children.child2().setUseKind(KnownCellUse);
@@ -905,6 +911,7 @@
case Call:
case Construct:
case GetByOffset:
+ case MultiGetByOffset:
case GetClosureVar:
case ArrayPop:
case ArrayPush:
@@ -1054,6 +1061,16 @@
return m_opInfo;
}
+ bool hasMultiGetByOffsetData()
+ {
+ return op() == MultiGetByOffset;
+ }
+
+ MultiGetByOffsetData& multiGetByOffsetData()
+ {
+ return *reinterpret_cast<MultiGetByOffsetData*>(m_opInfo);
+ }
+
bool hasFunctionDeclIndex()
{
return op() == NewFunction
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index dfa6c34..dc996d2 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -175,6 +175,7 @@
macro(ConstantStoragePointer, NodeResultStorage) \
macro(TypedArrayWatchpoint, NodeMustGenerate) \
macro(GetByOffset, NodeResultJS) \
+ macro(MultiGetByOffset, NodeResultJS) \
macro(PutByOffset, NodeMustGenerate) \
macro(GetArrayLength, NodeResultInt32) \
macro(GetTypedArrayByteOffset, NodeResultInt32) \
diff --git a/Source/JavaScriptCore/dfg/DFGPhase.h b/Source/JavaScriptCore/dfg/DFGPhase.h
index 6de043b..fb5ebb9 100644
--- a/Source/JavaScriptCore/dfg/DFGPhase.h
+++ b/Source/JavaScriptCore/dfg/DFGPhase.h
@@ -51,6 +51,8 @@
const char* name() const { return m_name; }
+ Graph& graph() { return m_graph; }
+
// Each phase must have a run() method.
protected:
@@ -73,7 +75,7 @@
bool runAndLog(PhaseType& phase)
{
bool result = phase.run();
- if (result && logCompilationChanges())
+ if (result && logCompilationChanges(phase.graph().m_plan.mode))
dataLogF("Phase %s changed the IR.\n", phase.name());
return result;
}
diff --git a/Source/JavaScriptCore/dfg/DFGPlan.cpp b/Source/JavaScriptCore/dfg/DFGPlan.cpp
index 58c6753..4a6a617 100644
--- a/Source/JavaScriptCore/dfg/DFGPlan.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPlan.cpp
@@ -85,7 +85,7 @@
static void dumpAndVerifyGraph(Graph& graph, const char* text)
{
GraphDumpMode modeForFinalValidate = DumpGraph;
- if (verboseCompilationEnabled()) {
+ if (verboseCompilationEnabled(graph.m_plan.mode)) {
dataLog(text, "\n");
graph.dump();
modeForFinalValidate = DontDumpGraph;
@@ -149,7 +149,7 @@
SamplingRegion samplingRegion("DFG Compilation (Plan)");
CompilationScope compilationScope;
- if (logCompilationChanges())
+ if (logCompilationChanges(mode))
dataLog("DFG(Plan) compiling ", *codeBlock, " with ", mode, ", number of instructions = ", codeBlock->instructionCount(), "\n");
CompilationPath path = compileInThreadImpl(longLivedState);
@@ -183,7 +183,7 @@
Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState)
{
- if (verboseCompilationEnabled() && osrEntryBytecodeIndex != UINT_MAX) {
+ if (verboseCompilationEnabled(mode) && osrEntryBytecodeIndex != UINT_MAX) {
dataLog("\n");
dataLog("Compiler must handle OSR entry from bc#", osrEntryBytecodeIndex, " with values: ", mustHandleValues, "\n");
dataLog("\n");
@@ -230,7 +230,7 @@
unsigned count = 1;
dfg.m_fixpointState = FixpointNotConverged;
for (;; ++count) {
- if (logCompilationChanges())
+ if (logCompilationChanges(mode))
dataLogF("DFG beginning optimization fixpoint iteration #%u.\n", count);
bool changed = false;
@@ -250,7 +250,7 @@
performCPSRethreading(dfg);
}
- if (logCompilationChanges())
+ if (logCompilationChanges(mode))
dataLogF("DFG optimization fixpoint converged in %u iterations.\n", count);
dfg.m_fixpointState = FixpointConverged;
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 19d9428..780e646 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -177,6 +177,7 @@
case GetByIdFlush:
case GetMyArgumentByValSafe:
case GetByOffset:
+ case MultiGetByOffset:
case Call:
case Construct:
case GetGlobalVar:
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index ae3486a..a67db2f9 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -253,6 +253,7 @@
case CheckInBounds:
case ConstantStoragePointer:
case Check:
+ case MultiGetByOffset:
return true;
case GetByVal:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index fde2b14..38240c1 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -4689,6 +4689,7 @@
case Int52ToValue:
case CheckInBounds:
case ArithIMul:
+ case MultiGetByOffset:
RELEASE_ASSERT_NOT_REACHED();
break;
}
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 6ea2476..33ad3c6 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -5010,6 +5010,7 @@
case ExtractOSREntryLocal:
case CheckInBounds:
case ArithIMul:
+ case MultiGetByOffset:
RELEASE_ASSERT_NOT_REACHED();
break;
}
diff --git a/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
index 9d21dd1..8d9ff1d 100644
--- a/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
@@ -246,6 +246,7 @@
case Phantom:
case HardPhantom:
case MovHint:
+ case MultiGetByOffset:
// Don't count these uses.
break;
@@ -345,6 +346,7 @@
case Phantom:
case HardPhantom:
case MovHint:
+ case MultiGetByOffset:
// Don't count these uses.
break;
diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
index 74073e2..a965e8c 100644
--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
@@ -137,6 +137,7 @@
case NewArrayWithSize:
case GetById:
case ToThis:
+ case MultiGetByOffset:
// These are OK.
break;
case PutByIdDirect:
diff --git a/Source/JavaScriptCore/ftl/FTLCompile.cpp b/Source/JavaScriptCore/ftl/FTLCompile.cpp
index 034758a..27a544d 100644
--- a/Source/JavaScriptCore/ftl/FTLCompile.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCompile.cpp
@@ -247,7 +247,7 @@
OSRExitCompilationInfo& info = state.finalizer->osrExit[i];
OSRExit& exit = jitCode->osrExit[i];
- if (Options::verboseCompilation())
+ if (verboseCompilationEnabled())
dataLog("Handling OSR stackmap #", exit.m_stackmapID, " for ", exit.m_codeOrigin, "\n");
iter = recordMap.find(exit.m_stackmapID);
@@ -269,7 +269,7 @@
VirtualRegister(value.virtualRegister().offset() + localsOffset));
}
- if (Options::verboseCompilation()) {
+ if (verboseCompilationEnabled()) {
DumpContext context;
dataLog(" Exit values: ", inContext(exit.m_values, &context), "\n");
}
@@ -286,7 +286,7 @@
for (unsigned i = state.getByIds.size(); i--;) {
GetByIdDescriptor& getById = state.getByIds[i];
- if (Options::verboseCompilation())
+ if (verboseCompilationEnabled())
dataLog("Handling GetById stackmap #", getById.stackmapID(), "\n");
iter = recordMap.find(getById.stackmapID());
@@ -324,7 +324,7 @@
for (unsigned i = state.putByIds.size(); i--;) {
PutByIdDescriptor& putById = state.putByIds[i];
- if (Options::verboseCompilation())
+ if (verboseCompilationEnabled())
dataLog("Handling PutById stackmap #", putById.stackmapID(), "\n");
iter = recordMap.find(putById.stackmapID());
@@ -541,7 +541,7 @@
llvm->RunPassManager(modulePasses, state.module);
}
- if (DFG::shouldShowDisassembly() || DFG::verboseCompilationEnabled())
+ if (shouldShowDisassembly() || verboseCompilationEnabled())
state.dumpState("after optimization");
// FIXME: Need to add support for the case where JIT memory allocation failed.
@@ -577,7 +577,7 @@
state.jitCode->unwindInfo.parse(
state.compactUnwind, state.compactUnwindSize, state.generatedFunction);
- if (DFG::shouldShowDisassembly())
+ if (shouldShowDisassembly())
dataLog("Unwind info for ", CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::FTLJIT), ":\n ", state.jitCode->unwindInfo, "\n");
if (state.stackmapsSection.size()) {
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
index 2c31b0f..ba5f24a 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
@@ -464,6 +464,9 @@
case GetByOffset:
compileGetByOffset();
break;
+ case MultiGetByOffset:
+ compileMultiGetByOffset();
+ break;
case PutByOffset:
compilePutByOffset();
break;
@@ -3075,6 +3078,63 @@
offsetRelativeToBase(data.offset))));
}
+ void compileMultiGetByOffset()
+ {
+ LValue base = lowCell(m_node->child1());
+
+ MultiGetByOffsetData& data = m_node->multiGetByOffsetData();
+
+ Vector<LBasicBlock, 2> blocks(data.variants.size());
+ for (unsigned i = data.variants.size(); i--;)
+ blocks[i] = FTL_NEW_BLOCK(m_out, ("MultiGetByOffset case ", i));
+ LBasicBlock exit = FTL_NEW_BLOCK(m_out, ("MultiGetByOffset fail"));
+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("MultiGetByOffset continuation"));
+
+ Vector<SwitchCase, 2> cases;
+ for (unsigned i = data.variants.size(); i--;) {
+ GetByIdVariant variant = data.variants[i];
+ for (unsigned j = variant.structureSet().size(); j--;)
+ cases.append(SwitchCase(weakPointer(variant.structureSet()[j]), blocks[i]));
+ }
+ m_out.switchInstruction(m_out.loadPtr(base, m_heaps.JSCell_structure), cases, exit);
+
+ LBasicBlock lastNext = m_out.m_nextBlock;
+
+ Vector<ValueFromBlock, 2> results;
+ for (unsigned i = data.variants.size(); i--;) {
+ m_out.appendTo(blocks[i], i + 1 < data.variants.size() ? blocks[i + 1] : exit);
+
+ GetByIdVariant variant = data.variants[i];
+ LValue result;
+ if (variant.specificValue())
+ result = m_out.constInt64(JSValue::encode(variant.specificValue()));
+ else {
+ LValue propertyBase;
+ if (variant.chain())
+ propertyBase = weakPointer(variant.chain()->terminalPrototype());
+ else
+ propertyBase = base;
+ if (!isInlineOffset(variant.offset()))
+ propertyBase = m_out.loadPtr(propertyBase, m_heaps.JSObject_butterfly);
+ result = m_out.load64(
+ m_out.address(
+ m_heaps.properties[data.identifierNumber],
+ propertyBase,
+ offsetRelativeToBase(variant.offset())));
+ }
+
+ results.append(m_out.anchor(result));
+ m_out.jump(continuation);
+ }
+
+ m_out.appendTo(exit, continuation);
+ terminate(BadCache);
+ m_out.unreachable();
+
+ m_out.appendTo(continuation, lastNext);
+ setJSValue(m_out.phi(m_out.int64, results));
+ }
+
void compilePutByOffset()
{
StorageAccessData& data =
diff --git a/Source/JavaScriptCore/ftl/FTLState.h b/Source/JavaScriptCore/ftl/FTLState.h
index 4152fab..ed48ebc 100644
--- a/Source/JavaScriptCore/ftl/FTLState.h
+++ b/Source/JavaScriptCore/ftl/FTLState.h
@@ -30,6 +30,7 @@
#if ENABLE(FTL_JIT)
+#include "DFGCommon.h"
#include "DFGGraph.h"
#include "FTLAbbreviations.h"
#include "FTLGeneratedFunction.h"
@@ -42,6 +43,16 @@
namespace JSC { namespace FTL {
+inline bool verboseCompilationEnabled()
+{
+ return DFG::verboseCompilationEnabled(DFG::FTLMode);
+}
+
+inline bool showDisassembly()
+{
+ return DFG::shouldShowDisassembly(DFG::FTLMode);
+}
+
class State {
WTF_MAKE_NONCOPYABLE(State);
diff --git a/Source/JavaScriptCore/jsc.cpp b/Source/JavaScriptCore/jsc.cpp
index a36cd4e..f572770 100644
--- a/Source/JavaScriptCore/jsc.cpp
+++ b/Source/JavaScriptCore/jsc.cpp
@@ -238,6 +238,7 @@
static EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState*);
static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionFalse(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionEffectful42(ExecState*);
#if ENABLE(SAMPLING_FLAGS)
static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
@@ -373,6 +374,8 @@
putDirectNativeFunction(vm, this, Identifier(&vm, "DFGTrue"), 0, functionFalse, DFGTrue, DontEnum | JSC::Function);
+ addFunction(vm, "effectful42", functionEffectful42, 0);
+
JSArray* array = constructEmptyArray(globalExec(), 0);
for (size_t i = 0; i < arguments.size(); ++i)
array->putDirectIndex(globalExec(), i, jsString(globalExec(), arguments[i]));
@@ -727,6 +730,11 @@
return JSValue::encode(jsBoolean(false));
}
+EncodedJSValue JSC_HOST_CALL functionEffectful42(ExecState*)
+{
+ return JSValue::encode(jsNumber(42));
+}
+
// 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/runtime/IntendedStructureChain.cpp b/Source/JavaScriptCore/runtime/IntendedStructureChain.cpp
index 587756b..67714be 100644
--- a/Source/JavaScriptCore/runtime/IntendedStructureChain.cpp
+++ b/Source/JavaScriptCore/runtime/IntendedStructureChain.cpp
@@ -29,6 +29,7 @@
#include "CodeBlock.h"
#include "JSCInlines.h"
#include "StructureChain.h"
+#include <wtf/CommaPrinter.h>
namespace JSC {
@@ -145,5 +146,21 @@
visitor.appendUnbarrieredPointer(&m_vector[i]);
}
+void IntendedStructureChain::dump(PrintStream& out) const
+{
+ dumpInContext(out, 0);
+}
+
+void IntendedStructureChain::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+ out.print(
+ "(global = ", RawPointer(m_globalObject), ", head = ",
+ pointerDumpInContext(m_head, context), ", vector = [");
+ CommaPrinter comma;
+ for (unsigned i = 0; i < m_vector.size(); ++i)
+ out.print(comma, pointerDumpInContext(m_vector[i], context));
+ out.print("])");
+}
+
} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/IntendedStructureChain.h b/Source/JavaScriptCore/runtime/IntendedStructureChain.h
index 74c1e67..5c76157 100644
--- a/Source/JavaScriptCore/runtime/IntendedStructureChain.h
+++ b/Source/JavaScriptCore/runtime/IntendedStructureChain.h
@@ -35,6 +35,7 @@
class JSGlobalObject;
class StructureChain;
class VM;
+struct DumpContext;
class IntendedStructureChain : public RefCounted<IntendedStructureChain> {
public:
@@ -61,6 +62,8 @@
Structure* last() const { return m_vector.last(); }
void visitChildren(SlotVisitor&);
+ void dump(PrintStream&) const;
+ void dumpInContext(PrintStream&, DumpContext*) const;
private:
JSGlobalObject* m_globalObject;
diff --git a/Source/JavaScriptCore/runtime/Options.cpp b/Source/JavaScriptCore/runtime/Options.cpp
index 6333c77..b989cef 100644
--- a/Source/JavaScriptCore/runtime/Options.cpp
+++ b/Source/JavaScriptCore/runtime/Options.cpp
@@ -189,9 +189,11 @@
if (Options::showDisassembly()
|| Options::showDFGDisassembly()
+ || Options::showFTLDisassembly()
|| Options::dumpBytecodeAtDFGTime()
|| Options::dumpGraphAtEachPhase()
|| Options::verboseCompilation()
+ || Options::verboseFTLCompilation()
|| Options::logCompilationChanges()
|| Options::validateGraph()
|| Options::validateGraphAtEachPhase()
diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h
index e359aa7..2b14177 100644
--- a/Source/JavaScriptCore/runtime/Options.h
+++ b/Source/JavaScriptCore/runtime/Options.h
@@ -112,11 +112,13 @@
/* showDisassembly implies showDFGDisassembly. */ \
v(bool, showDisassembly, false) \
v(bool, showDFGDisassembly, false) \
+ v(bool, showFTLDisassembly, false) \
v(bool, showAllDFGNodes, false) \
v(optionRange, bytecodeRangeToDFGCompile, 0) \
v(bool, dumpBytecodeAtDFGTime, false) \
v(bool, dumpGraphAtEachPhase, false) \
v(bool, verboseCompilation, false) \
+ v(bool, verboseFTLCompilation, false) \
v(bool, logCompilationChanges, false) \
v(bool, printEachOSRExit, false) \
v(bool, validateGraph, false) \
diff --git a/Source/JavaScriptCore/tests/stress/fold-multi-get-by-offset-to-get-by-offset-with-watchpoint.js b/Source/JavaScriptCore/tests/stress/fold-multi-get-by-offset-to-get-by-offset-with-watchpoint.js
new file mode 100644
index 0000000..0973369
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/fold-multi-get-by-offset-to-get-by-offset-with-watchpoint.js
@@ -0,0 +1,26 @@
+//@ defaultNoEagerRun
+
+function foo(o) {
+ return o.f;
+}
+
+for (var i = 0; i < 100; ++i) {
+ var result = foo((i & 1) ? {f:1, g:2} : {g:1, f:2});
+ if (result != 2 - (i & 1))
+ throw "Error: bad result in warm-up loop for i = " + i + ": " + result;
+}
+
+function bar(o) {
+ return o.g + effectful42() + foo(o);
+}
+
+noInline(bar);
+
+for (var i = 0; i < 100000; ++i) {
+ var result = bar({f:i, g:i * 3});
+ if (result != i * 4 + 42)
+ throw "Error: bad result for i = " + i + ": " + result;
+}
+
+if (reoptimizationRetryCount(bar))
+ throw "Error: reoptimized bar unexpectedly: " + reoptimizationRetryCount(bar);
diff --git a/Source/JavaScriptCore/tests/stress/fold-multi-get-by-offset-to-get-by-offset.js b/Source/JavaScriptCore/tests/stress/fold-multi-get-by-offset-to-get-by-offset.js
new file mode 100644
index 0000000..34be8e9
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/fold-multi-get-by-offset-to-get-by-offset.js
@@ -0,0 +1,26 @@
+//@ defaultNoEagerRun
+
+function foo(o) {
+ return o.f;
+}
+
+for (var i = 0; i < 100; ++i) {
+ var result = foo((i & 1) ? {f:1, g:2} : {g:1, f:2});
+ if (result != 2 - (i & 1))
+ throw "Error: bad result in warm-up loop for i = " + i + ": " + result;
+}
+
+function bar(o) {
+ return o.g + foo(o);
+}
+
+noInline(bar);
+
+for (var i = 0; i < 100000; ++i) {
+ var result = bar({f:i, g:i * 3});
+ if (result != i * 4)
+ throw "Error: bad result for i = " + i + ": " + result;
+}
+
+if (reoptimizationRetryCount(bar))
+ throw "Error: reoptimized bar unexpectedly: " + reoptimizationRetryCount(bar);
diff --git a/Source/JavaScriptCore/tests/stress/multi-get-by-offset-proto-and-self.js b/Source/JavaScriptCore/tests/stress/multi-get-by-offset-proto-and-self.js
new file mode 100644
index 0000000..e4510b3
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/multi-get-by-offset-proto-and-self.js
@@ -0,0 +1,20 @@
+function foo(o) {
+ return o.f;
+}
+
+noInline(foo);
+
+function Foo() { }
+Foo.prototype.f = 42;
+
+for (var i = 0; i < 100000; ++i) {
+ if (i & 1) {
+ var result = foo(new Foo());
+ if (result != 42)
+ throw "Error: bad result for new Foo(): " + result;
+ } else {
+ var result = foo({f:24});
+ if (result != 24)
+ throw "Error: bad result for {f:24}: " + result;
+ }
+}
diff --git a/Source/WTF/ChangeLog b/Source/WTF/ChangeLog
index 639a49a..c94cd17 100644
--- a/Source/WTF/ChangeLog
+++ b/Source/WTF/ChangeLog
@@ -1,3 +1,15 @@
+2014-02-15 Filip Pizlo <fpizlo@apple.com>
+
+ FTL should inline polymorphic heap accesses
+ https://bugs.webkit.org/show_bug.cgi?id=128795
+
+ Reviewed by Oliver Hunt.
+
+ * wtf/PrintStream.h:
+ (WTF::PointerDumpInContext::PointerDumpInContext):
+ (WTF::PointerDumpInContext::dump):
+ (WTF::pointerDumpInContext):
+
2014-02-16 Zan Dobersek <zdobersek@igalia.com>
Reintroduce const qualifiers for return types of (Filter|Transform)Iterator::operator*()
diff --git a/Source/WTF/wtf/PrintStream.h b/Source/WTF/wtf/PrintStream.h
index 329ad07..8a06b5e 100644
--- a/Source/WTF/wtf/PrintStream.h
+++ b/Source/WTF/wtf/PrintStream.h
@@ -365,6 +365,34 @@
}
template<typename T, typename U>
+class PointerDumpInContext {
+public:
+ PointerDumpInContext(const T* ptr, U* context)
+ : m_ptr(ptr)
+ , m_context(context)
+ {
+ }
+
+ void dump(PrintStream& out) const
+ {
+ if (m_ptr)
+ m_ptr->dumpInContext(out, m_context);
+ else
+ out.print("(null)");
+ }
+
+private:
+ const T* m_ptr;
+ U* m_context;
+};
+
+template<typename T, typename U>
+PointerDumpInContext<T, U> pointerDumpInContext(const T* ptr, U* context)
+{
+ return PointerDumpInContext<T, U>(ptr, context);
+}
+
+template<typename T, typename U>
class ValueIgnoringContext {
public:
ValueIgnoringContext(const U& value)
@@ -396,6 +424,7 @@
using WTF::ignoringContext;
using WTF::inContext;
using WTF::pointerDump;
+using WTF::pointerDumpInContext;
#endif // PrintStream_h