FTL should inline polymorphic heap accesses
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 17 Feb 2014 06:35:32 +0000 (06:35 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 17 Feb 2014 06:35:32 +0000 (06:35 +0000)
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

49 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/polymorphic-get-by-id-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/polymorphic-get-by-id.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/polymorphic-get-by-id.js [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/GNUmakefile.list.am
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/ExitingJITType.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/ExitingJITType.h
Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
Source/JavaScriptCore/bytecode/GetByIdStatus.h
Source/JavaScriptCore/bytecode/GetByIdVariant.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/GetByIdVariant.h [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGCommon.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDriver.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGGraph.h
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPhase.h
Source/JavaScriptCore/dfg/DFGPlan.cpp
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLCompile.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/ftl/FTLState.h
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/IntendedStructureChain.cpp
Source/JavaScriptCore/runtime/IntendedStructureChain.h
Source/JavaScriptCore/runtime/Options.cpp
Source/JavaScriptCore/runtime/Options.h
Source/JavaScriptCore/tests/stress/fold-multi-get-by-offset-to-get-by-offset-with-watchpoint.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/fold-multi-get-by-offset-to-get-by-offset.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/multi-get-by-offset-proto-and-self.js [new file with mode: 0644]
Source/WTF/ChangeLog
Source/WTF/wtf/PrintStream.h

index 07d7505..55b6cff 100644 (file)
@@ -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.
+
+        * 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):
+
 2014-02-16  Filip Pizlo  <fpizlo@apple.com>
 
         Unreviewed, add a useful comment for this test.
diff --git a/LayoutTests/js/regress/polymorphic-get-by-id-expected.txt b/LayoutTests/js/regress/polymorphic-get-by-id-expected.txt
new file mode 100644 (file)
index 0000000..0f7ca2a
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/polymorphic-get-by-id
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/polymorphic-get-by-id.html b/LayoutTests/js/regress/polymorphic-get-by-id.html
new file mode 100644 (file)
index 0000000..4168078
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="resources/regress-pre.js"></script>
+<script src="script-tests/polymorphic-get-by-id.js"></script>
+<script src="resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/polymorphic-get-by-id.js b/LayoutTests/js/regress/script-tests/polymorphic-get-by-id.js
new file mode 100644 (file)
index 0000000..07a7806
--- /dev/null
@@ -0,0 +1,21 @@
+function foo(o) {
+    var result = 0;
+    for (var i = 0; i < 100; ++i)
+        result += o.f;
+    return result;
+}
+
+noInline(foo);
+
+var o = {f:1, g:2};
+var p = {g:1, f:2};
+for (var i = 0; i < 10000; ++i) {
+    var result = foo((i & 1) ? o : p);
+    if (result != ((i & 1) ? 100 : 200))
+        throw "Error: bad result for i = " + i + ": " + result;
+}
+
+var q = {g:3, h:4, f:5};
+var result = foo(q);
+if (result != 500)
+    throw "Error: bad result at end: " + result;
index 1db2094..cf35c76 100644 (file)
@@ -70,7 +70,9 @@ set(JavaScriptCore_SOURCES
     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
index 1fbe6ec..61dd10b 100644 (file)
@@ -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
index 5682d82..60049b8 100644 (file)
@@ -147,10 +147,14 @@ javascriptcore_sources += \
        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 \
index 7954ec2..023cfad 100644 (file)
     <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" />
     <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" />
index 9bdc910..dded8a3 100644 (file)
@@ -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 */; };
                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, ); }; };
 /* 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>"; };
                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>"; };
                                969A07920ED1D3AE00F1F681 /* EvalCodeCache.h */,
                                0F56A1D415001CF2002992B1 /* ExecutionCounter.cpp */,
                                0F56A1D115000F31002992B1 /* ExecutionCounter.h */,
+                               0F0332BF18ADFAE1005F979A /* ExitingJITType.cpp */,
                                0F3AC753188E5EC80032029F /* ExitingJITType.h */,
                                0FB105821675480C00F8AB6E /* ExitKind.cpp */,
                                0FB105831675480C00F8AB6E /* ExitKind.h */,
                                0F666EBF183566F900D017F1 /* FullBytecodeLiveness.h */,
                                0F93329514CA7DC10085F3C6 /* GetByIdStatus.cpp */,
                                0F93329614CA7DC10085F3C6 /* GetByIdStatus.h */,
+                               0F0332C118B01763005F979A /* GetByIdVariant.cpp */,
+                               0F0332C218B01763005F979A /* GetByIdVariant.h */,
                                0F0B83A814BCF55E00885B4F /* HandlerInfo.h */,
                                0F24E55317F0B71C00ABB217 /* InlineCallFrameSet.cpp */,
                                0F24E55417F0B71C00ABB217 /* InlineCallFrameSet.h */,
                                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 */,
                                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 */,
                                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 */,
index b576cd4..cdd668a 100644 (file)
@@ -1377,7 +1377,7 @@ void CodeBlock::dumpBytecode(PrintStream& out, ExecState* exec, const Instructio
         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 (file)
index 0000000..aa8f120
--- /dev/null
@@ -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
+
index fbdc28e..e8ed03e 100644 (file)
@@ -51,5 +51,12 @@ inline ExitingJITType exitingJITTypeFor(JITCode::JITType type)
 
 } // namespace JSC
 
+namespace WTF {
+
+class PrintStream;
+void printInternal(PrintStream&, JSC::ExitingJITType);
+
+} // namespace WTF
+
 #endif // ExitingJITType_h
 
index b8889d7..6e4e802 100644 (file)
@@ -31,6 +31,7 @@
 #include "LLIntData.h"
 #include "LowLevelInterpreter.h"
 #include "JSCInlines.h"
+#include <wtf/ListDump.h>
 
 namespace JSC {
 
@@ -71,15 +72,17 @@ GetByIdStatus GetByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned
     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 @@ void GetByIdStatus::computeForChain(GetByIdStatus& result, CodeBlock* profiledBl
     // 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 @@ GetByIdStatus GetByIdStatus::computeForStubInfo(
     
     // 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 @@ GetByIdStatus GetByIdStatus::computeForStubInfo(
             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 @@ GetByIdStatus GetByIdStatus::computeForStubInfo(
             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 @@ GetByIdStatus GetByIdStatus::computeFor(
             ConcurrentJITLocker locker(dfgBlock->m_lock);
             result = computeForStubInfo(locker, dfgBlock, dfgMap.get(codeOrigin), uid);
         }
-    
+        
         if (result.takesSlowPath())
             return result;
     
@@ -318,21 +334,37 @@ GetByIdStatus GetByIdStatus::computeFor(VM& vm, Structure* structure, StringImpl
     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
index 685fa1b..94f4dbb 100644 (file)
@@ -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 @@ public:
 
     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 @@ public:
     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 @@ private:
 #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 (file)
index 0000000..b8bedce
--- /dev/null
@@ -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 (file)
index 0000000..f0201ee
--- /dev/null
@@ -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
+
index 7035890..df93d07 100644 (file)
@@ -1451,17 +1451,17 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 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 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         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;
index 0254bb2..dbb6492 100644 (file)
@@ -180,6 +180,7 @@ private:
     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 @@ Node* ByteCodeParser::handlePutByOffset(Node* base, unsigned identifier, Propert
     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 @@ void ByteCodeParser::handleGetById(
         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 @@ void ByteCodeParser::handleGetById(
     // 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 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             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));
index d6db78b..f410fa5 100644 (file)
@@ -642,23 +642,29 @@ private:
         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 @@ private:
         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:
index dc7d1eb..55db57d 100644 (file)
@@ -470,6 +470,10 @@ void clobberize(Graph& graph, Node* node, ReadFunctor& read, WriteFunctor& write
         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;
index e9eacbd..9c8a934 100644 (file)
@@ -28,6 +28,8 @@
 
 #include <wtf/Platform.h>
 
+#include "DFGCompilationMode.h"
+
 #if ENABLE(DFG_JIT)
 
 #include "CodeOrigin.h"
@@ -63,14 +65,14 @@ enum RefNodeMode {
     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 @@ inline CapabilityLevel leastUpperBound(CapabilityLevel a, CapabilityLevel b)
 }
 
 // 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
index d4d7330..a8056cb 100644 (file)
@@ -151,10 +151,33 @@ private:
                 
                 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 @@ private:
                 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 @@ private:
         
         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)
     {
index b65e515..b29df5f 100644 (file)
@@ -82,7 +82,7 @@ static CompilationResult compileImpl(
     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 @@ static CompilationResult compileImpl(
     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;
index 33c7cef..0ab2b37 100644 (file)
@@ -881,6 +881,11 @@ private:
             break;
         }
             
+        case MultiGetByOffset: {
+            fixEdge<CellUse>(node->child1());
+            break;
+        }
+            
         case PutByOffset: {
             if (!node->child1()->hasStorageResult())
                 fixEdge<KnownCellUse>(node->child1());
index 5fe44ad..df49ece 100644 (file)
@@ -246,6 +246,12 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node, DumpContext*
         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();
index 1631ecc..8725d14 100644 (file)
@@ -177,7 +177,10 @@ public:
     
     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 @@ public:
     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;
index beddaed..d998600 100644 (file)
@@ -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 @@ namespace JSC { namespace DFG {
 class Graph;
 struct BasicBlock;
 
+struct MultiGetByOffsetData {
+    unsigned identifierNumber;
+    Vector<GetByIdVariant, 2> variants;
+};
+
 struct StructureTransitionData {
     Structure* previousStructure;
     Structure* newStructure;
@@ -406,7 +412,7 @@ struct Node {
     
     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 @@ struct Node {
         case Call:
         case Construct:
         case GetByOffset:
+        case MultiGetByOffset:
         case GetClosureVar:
         case ArrayPop:
         case ArrayPush:
@@ -1054,6 +1061,16 @@ struct Node {
         return m_opInfo;
     }
     
+    bool hasMultiGetByOffsetData()
+    {
+        return op() == MultiGetByOffset;
+    }
+    
+    MultiGetByOffsetData& multiGetByOffsetData()
+    {
+        return *reinterpret_cast<MultiGetByOffsetData*>(m_opInfo);
+    }
+    
     bool hasFunctionDeclIndex()
     {
         return op() == NewFunction
index dfa6c34..dc996d2 100644 (file)
@@ -175,6 +175,7 @@ namespace JSC { namespace DFG {
     macro(ConstantStoragePointer, NodeResultStorage) \
     macro(TypedArrayWatchpoint, NodeMustGenerate) \
     macro(GetByOffset, NodeResultJS) \
+    macro(MultiGetByOffset, NodeResultJS) \
     macro(PutByOffset, NodeMustGenerate) \
     macro(GetArrayLength, NodeResultInt32) \
     macro(GetTypedArrayByteOffset, NodeResultInt32) \
index 6de043b..fb5ebb9 100644 (file)
@@ -51,6 +51,8 @@ public:
     
     const char* name() const { return m_name; }
     
+    Graph& graph() { return m_graph; }
+    
     // Each phase must have a run() method.
     
 protected:
@@ -73,7 +75,7 @@ template<typename PhaseType>
 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;
 }
index 58c6753..4a6a617 100644 (file)
@@ -85,7 +85,7 @@ namespace JSC { namespace DFG {
 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 @@ void Plan::compileInThread(LongLivedState& longLivedState, ThreadData* threadDat
     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 @@ void Plan::compileInThread(LongLivedState& longLivedState, ThreadData* threadDat
 
 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 @@ Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState)
     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 @@ Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState)
         performCPSRethreading(dfg);
     }
     
-    if (logCompilationChanges())
+    if (logCompilationChanges(mode))
         dataLogF("DFG optimization fixpoint converged in %u iterations.\n", count);
 
     dfg.m_fixpointState = FixpointConverged;
index 19d9428..780e646 100644 (file)
@@ -177,6 +177,7 @@ private:
         case GetByIdFlush:
         case GetMyArgumentByValSafe:
         case GetByOffset:
+        case MultiGetByOffset:
         case Call:
         case Construct:
         case GetGlobalVar:
index ae3486a..a67db2f 100644 (file)
@@ -253,6 +253,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case CheckInBounds:
     case ConstantStoragePointer:
     case Check:
+    case MultiGetByOffset:
         return true;
         
     case GetByVal:
index fde2b14..38240c1 100644 (file)
@@ -4689,6 +4689,7 @@ void SpeculativeJIT::compile(Node* node)
     case Int52ToValue:
     case CheckInBounds:
     case ArithIMul:
+    case MultiGetByOffset:
         RELEASE_ASSERT_NOT_REACHED();
         break;
     }
index 6ea2476..33ad3c6 100644 (file)
@@ -5010,6 +5010,7 @@ void SpeculativeJIT::compile(Node* node)
     case ExtractOSREntryLocal:
     case CheckInBounds:
     case ArithIMul:
+    case MultiGetByOffset:
         RELEASE_ASSERT_NOT_REACHED();
         break;
     }
index 9d21dd1..8d9ff1d 100644 (file)
@@ -246,6 +246,7 @@ private:
                 case Phantom:
                 case HardPhantom:
                 case MovHint:
+                case MultiGetByOffset:
                     // Don't count these uses.
                     break;
                     
@@ -345,6 +346,7 @@ private:
                 case Phantom:
                 case HardPhantom:
                 case MovHint:
+                case MultiGetByOffset:
                     // Don't count these uses.
                     break;
                     
index 74073e2..a965e8c 100644 (file)
@@ -137,6 +137,7 @@ inline CapabilityLevel canCompile(Node* node)
     case NewArrayWithSize:
     case GetById:
     case ToThis:
+    case MultiGetByOffset:
         // These are OK.
         break;
     case PutByIdDirect:
index 034758a..27a544d 100644 (file)
@@ -247,7 +247,7 @@ static void fixFunctionBasedOnStackMaps(
             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 @@ static void fixFunctionBasedOnStackMaps(
                     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 @@ static void fixFunctionBasedOnStackMaps(
         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 @@ static void fixFunctionBasedOnStackMaps(
         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 @@ void compile(State& state)
             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 @@ void compile(State& state)
     
     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()) {
index 2c31b0f..ba5f24a 100644 (file)
@@ -464,6 +464,9 @@ private:
         case GetByOffset:
             compileGetByOffset();
             break;
+        case MultiGetByOffset:
+            compileMultiGetByOffset();
+            break;
         case PutByOffset:
             compilePutByOffset();
             break;
@@ -3075,6 +3078,63 @@ private:
                     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 =
index 4152fab..ed48ebc 100644 (file)
@@ -30,6 +30,7 @@
 
 #if ENABLE(FTL_JIT)
 
+#include "DFGCommon.h"
 #include "DFGGraph.h"
 #include "FTLAbbreviations.h"
 #include "FTLGeneratedFunction.h"
 
 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);
     
index a36cd4e..f572770 100644 (file)
@@ -238,6 +238,7 @@ static EncodedJSValue JSC_HOST_CALL functionReoptimizationRetryCount(ExecState*)
 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 @@ protected:
         
         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 @@ EncodedJSValue JSC_HOST_CALL functionFalse(ExecState*)
     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
index 587756b..67714be 100644 (file)
@@ -29,6 +29,7 @@
 #include "CodeBlock.h"
 #include "JSCInlines.h"
 #include "StructureChain.h"
+#include <wtf/CommaPrinter.h>
 
 namespace JSC {
 
@@ -145,5 +146,21 @@ void IntendedStructureChain::visitChildren(SlotVisitor& visitor)
         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
 
index 74c1e67..5c76157 100644 (file)
@@ -35,6 +35,7 @@ class CodeBlock;
 class JSGlobalObject;
 class StructureChain;
 class VM;
+struct DumpContext;
 
 class IntendedStructureChain : public RefCounted<IntendedStructureChain> {
 public:
@@ -61,6 +62,8 @@ public:
     Structure* last() const { return m_vector.last(); }
     
     void visitChildren(SlotVisitor&);
+    void dump(PrintStream&) const;
+    void dumpInContext(PrintStream&, DumpContext*) const;
     
 private:
     JSGlobalObject* m_globalObject;
index 6333c77..b989cef 100644 (file)
@@ -189,9 +189,11 @@ static void recomputeDependentOptions()
     
     if (Options::showDisassembly()
         || Options::showDFGDisassembly()
+        || Options::showFTLDisassembly()
         || Options::dumpBytecodeAtDFGTime()
         || Options::dumpGraphAtEachPhase()
         || Options::verboseCompilation()
+        || Options::verboseFTLCompilation()
         || Options::logCompilationChanges()
         || Options::validateGraph()
         || Options::validateGraphAtEachPhase()
index e359aa7..2b14177 100644 (file)
@@ -112,11 +112,13 @@ typedef OptionRange optionRange;
     /* 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 (file)
index 0000000..0973369
--- /dev/null
@@ -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 (file)
index 0000000..34be8e9
--- /dev/null
@@ -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 (file)
index 0000000..e4510b3
--- /dev/null
@@ -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;
+    }
+}
index 639a49a..c94cd17 100644 (file)
@@ -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*()
index 329ad07..8a06b5e 100644 (file)
@@ -365,6 +365,34 @@ ValueInContext<T, U> inContext(const T& value, U* context)
 }
 
 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::PrintStream;
 using WTF::ignoringContext;
 using WTF::inContext;
 using WTF::pointerDump;
+using WTF::pointerDumpInContext;
 
 #endif // PrintStream_h