DFG SSA stack accesses shouldn't speak of VariableAccessDatas
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Feb 2015 19:51:52 +0000 (19:51 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Feb 2015 19:51:52 +0000 (19:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142036

Reviewed by Michael Saboff.

VariableAccessData is a useful thing in LoadStore and ThreadedCPS, but it's purely harmful in
SSA because you can't cook up new VariableAccessDatas. So, if you know that you want to load
or store to the stack, and you know what format to use as well as the location, then prior to
this patch you couldn't do it unless you found some existing VariableAccessData that matched
your requirements. That can be a hard task.

It's better if SSA doesn't speak of VariableAccessDatas but instead just has stack accesses
that speak of the things that a stack access needs: local, machineLocal, and format. This
patch changes the SSA way of accessing the stack to do just that.

Also add more IR validation.

* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGFlushFormat.h:
(JSC::DFG::isConcrete):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGGraph.h:
* dfg/DFGMayExit.cpp:
(JSC::DFG::mayExit):
* dfg/DFGNode.cpp:
(JSC::DFG::Node::hasVariableAccessData):
* dfg/DFGNode.h:
(JSC::DFG::StackAccessData::StackAccessData):
(JSC::DFG::StackAccessData::flushedAt):
(JSC::DFG::Node::convertToPutStack):
(JSC::DFG::Node::convertToGetStack):
(JSC::DFG::Node::hasUnlinkedLocal):
(JSC::DFG::Node::hasStackAccessData):
(JSC::DFG::Node::stackAccessData):
(JSC::DFG::Node::willHaveCodeGenOrOSR):
* dfg/DFGNodeType.h:
* dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
(JSC::DFG::LocalOSRAvailabilityCalculator::executeNode):
* dfg/DFGPlan.cpp:
(JSC::DFG::Plan::compileInThreadImpl):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGPutLocalSinkingPhase.cpp: Removed.
* dfg/DFGPutLocalSinkingPhase.h: Removed.
* dfg/DFGPutStackSinkingPhase.cpp: Copied from Source/JavaScriptCore/dfg/DFGPutLocalSinkingPhase.cpp.
(JSC::DFG::performPutStackSinking):
(JSC::DFG::performPutLocalSinking): Deleted.
* dfg/DFGPutStackSinkingPhase.h: Copied from Source/JavaScriptCore/dfg/DFGPutLocalSinkingPhase.h.
* dfg/DFGSSAConversionPhase.cpp:
(JSC::DFG::SSAConversionPhase::run):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStackLayoutPhase.cpp:
(JSC::DFG::StackLayoutPhase::run):
* dfg/DFGValidate.cpp:
(JSC::DFG::Validate::validate):
(JSC::DFG::Validate::validateCPS):
(JSC::DFG::Validate::validateSSA):
* dfg/DFGVirtualRegisterAllocationPhase.cpp:
(JSC::DFG::VirtualRegisterAllocationPhase::run):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::lower):
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileGetStack):
(JSC::FTL::LowerDFGToLLVM::compilePutStack):
(JSC::FTL::LowerDFGToLLVM::compileGetLocal): Deleted.
(JSC::FTL::LowerDFGToLLVM::compilePutLocal): Deleted.
* ftl/FTLOSRExit.h:
* tests/stress/many-sunken-locals.js: Added. This failure mode was caught by some miscellaneous test, so I figured I should write an explicit test for it.
(foo):
(bar):
(baz):
(fuzz):
(buzz):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@180691 268f45cc-cd09-0410-ab3c-d52691b4dbfc

32 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGFlushFormat.h
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGGraph.h
Source/JavaScriptCore/dfg/DFGMayExit.cpp
Source/JavaScriptCore/dfg/DFGNode.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp
Source/JavaScriptCore/dfg/DFGPlan.cpp
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.cpp [moved from Source/JavaScriptCore/dfg/DFGPutLocalSinkingPhase.cpp with 70% similarity]
Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.h [moved from Source/JavaScriptCore/dfg/DFGPutLocalSinkingPhase.h with 81% similarity]
Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGStackLayoutPhase.cpp
Source/JavaScriptCore/dfg/DFGValidate.cpp
Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/ftl/FTLOSRExit.h
Source/JavaScriptCore/tests/stress/many-sunken-locals.js [new file with mode: 0644]

index 9cfaabf..f9cbddc 100644 (file)
@@ -206,7 +206,7 @@ set(JavaScriptCore_SOURCES
     dfg/DFGPredictionPropagationPhase.cpp
     dfg/DFGPromotedHeapLocation.cpp
     dfg/DFGPureValue.cpp
-    dfg/DFGPutLocalSinkingPhase.cpp
+    dfg/DFGPutStackSinkingPhase.cpp
     dfg/DFGResurrectionForValidationPhase.cpp
     dfg/DFGSSACalculator.cpp
     dfg/DFGSSAConversionPhase.cpp
index 3f7bac8..536e229 100644 (file)
@@ -1,3 +1,99 @@
+2015-02-25  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG SSA stack accesses shouldn't speak of VariableAccessDatas
+        https://bugs.webkit.org/show_bug.cgi?id=142036
+
+        Reviewed by Michael Saboff.
+        
+        VariableAccessData is a useful thing in LoadStore and ThreadedCPS, but it's purely harmful in
+        SSA because you can't cook up new VariableAccessDatas. So, if you know that you want to load
+        or store to the stack, and you know what format to use as well as the location, then prior to
+        this patch you couldn't do it unless you found some existing VariableAccessData that matched
+        your requirements. That can be a hard task.
+        
+        It's better if SSA doesn't speak of VariableAccessDatas but instead just has stack accesses
+        that speak of the things that a stack access needs: local, machineLocal, and format. This
+        patch changes the SSA way of accessing the stack to do just that.
+        
+        Also add more IR validation.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGFlushFormat.h:
+        (JSC::DFG::isConcrete):
+        * dfg/DFGGraph.cpp:
+        (JSC::DFG::Graph::dump):
+        * dfg/DFGGraph.h:
+        * dfg/DFGMayExit.cpp:
+        (JSC::DFG::mayExit):
+        * dfg/DFGNode.cpp:
+        (JSC::DFG::Node::hasVariableAccessData):
+        * dfg/DFGNode.h:
+        (JSC::DFG::StackAccessData::StackAccessData):
+        (JSC::DFG::StackAccessData::flushedAt):
+        (JSC::DFG::Node::convertToPutStack):
+        (JSC::DFG::Node::convertToGetStack):
+        (JSC::DFG::Node::hasUnlinkedLocal):
+        (JSC::DFG::Node::hasStackAccessData):
+        (JSC::DFG::Node::stackAccessData):
+        (JSC::DFG::Node::willHaveCodeGenOrOSR):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
+        (JSC::DFG::LocalOSRAvailabilityCalculator::executeNode):
+        * dfg/DFGPlan.cpp:
+        (JSC::DFG::Plan::compileInThreadImpl):
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGPutLocalSinkingPhase.cpp: Removed.
+        * dfg/DFGPutLocalSinkingPhase.h: Removed.
+        * dfg/DFGPutStackSinkingPhase.cpp: Copied from Source/JavaScriptCore/dfg/DFGPutLocalSinkingPhase.cpp.
+        (JSC::DFG::performPutStackSinking):
+        (JSC::DFG::performPutLocalSinking): Deleted.
+        * dfg/DFGPutStackSinkingPhase.h: Copied from Source/JavaScriptCore/dfg/DFGPutLocalSinkingPhase.h.
+        * dfg/DFGSSAConversionPhase.cpp:
+        (JSC::DFG::SSAConversionPhase::run):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStackLayoutPhase.cpp:
+        (JSC::DFG::StackLayoutPhase::run):
+        * dfg/DFGValidate.cpp:
+        (JSC::DFG::Validate::validate):
+        (JSC::DFG::Validate::validateCPS):
+        (JSC::DFG::Validate::validateSSA):
+        * dfg/DFGVirtualRegisterAllocationPhase.cpp:
+        (JSC::DFG::VirtualRegisterAllocationPhase::run):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::LowerDFGToLLVM::lower):
+        (JSC::FTL::LowerDFGToLLVM::compileNode):
+        (JSC::FTL::LowerDFGToLLVM::compileGetStack):
+        (JSC::FTL::LowerDFGToLLVM::compilePutStack):
+        (JSC::FTL::LowerDFGToLLVM::compileGetLocal): Deleted.
+        (JSC::FTL::LowerDFGToLLVM::compilePutLocal): Deleted.
+        * ftl/FTLOSRExit.h:
+        * tests/stress/many-sunken-locals.js: Added. This failure mode was caught by some miscellaneous test, so I figured I should write an explicit test for it.
+        (foo):
+        (bar):
+        (baz):
+        (fuzz):
+        (buzz):
+
 2015-02-26  Mark Lam  <mark.lam@apple.com>
 
         Rolling out r180602, r180608, r180613, r180617, r180671.
index 73e1547..831070c 100644 (file)
     <ClCompile Include="..\dfg\DFGPredictionPropagationPhase.cpp" />
     <ClCompile Include="..\dfg\DFGPromotedHeapLocation.cpp" />
     <ClCompile Include="..\dfg\DFGPureValue.cpp" />
-    <ClCompile Include="..\dfg\DFGPutLocalSinkingPhase.cpp" />
+    <ClCompile Include="..\dfg\DFGPutStackSinkingPhase.cpp" />
     <ClCompile Include="..\dfg\DFGResurrectionForValidationPhase.cpp" />
     <ClCompile Include="..\dfg\DFGSafepoint.cpp" />
     <ClCompile Include="..\dfg\DFGSpeculativeJIT.cpp" />
     <ClInclude Include="..\dfg\DFGPromoteHeapAccess.h" />
     <ClInclude Include="..\dfg\DFGPromotedHeapLocation.h" />
     <ClInclude Include="..\dfg\DFGPureValue.h" />
-    <ClInclude Include="..\dfg\DFGPutLocalSinkingPhase.h" />
+    <ClInclude Include="..\dfg\DFGPutStackSinkingPhase.h" />
     <ClInclude Include="..\dfg\DFGRegisterBank.h" />
     <ClInclude Include="..\dfg\DFGRegisterSet.h" />
     <ClInclude Include="..\dfg\DFGResurrectionForValidationPhase.h" />
index cad3333..6a84565 100644 (file)
                0F38B01817CFE75500B144D3 /* DFGCompilationKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F38B01417CFE75500B144D3 /* DFGCompilationKey.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F38B01917CFE75500B144D3 /* DFGCompilationMode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F38B01517CFE75500B144D3 /* DFGCompilationMode.cpp */; };
                0F38B01A17CFE75500B144D3 /* DFGCompilationMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F38B01617CFE75500B144D3 /* DFGCompilationMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F3A1BF91A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3A1BF71A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp */; };
+               0F3A1BFA1A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3A1BF81A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F3AC752183EA1040032029F /* StackAlignment.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3AC751183EA1040032029F /* StackAlignment.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F3AC754188E5EC80032029F /* ExitingJITType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3AC753188E5EC80032029F /* ExitingJITType.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F3B3A1A153E68F2003ED0FF /* DFGConstantFoldingPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3B3A17153E68EF003ED0FF /* DFGConstantFoldingPhase.cpp */; };
                C4F4B6F51A05C984005CAB76 /* generate_objc_protocol_types_implementation.py in Headers */ = {isa = PBXBuildFile; fileRef = C4F4B6D71A05C76F005CAB76 /* generate_objc_protocol_types_implementation.py */; settings = {ATTRIBUTES = (Private, ); }; };
                C4F4B6F61A05C984005CAB76 /* objc_generator_templates.py in Headers */ = {isa = PBXBuildFile; fileRef = C4F4B6D81A05C76F005CAB76 /* objc_generator_templates.py */; settings = {ATTRIBUTES = (Private, ); }; };
                DC00039319D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h in Headers */ = {isa = PBXBuildFile; fileRef = DC00039019D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               DC00039819DBA70600023EB0 /* DFGPutLocalSinkingPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC00039619DBA70600023EB0 /* DFGPutLocalSinkingPhase.cpp */; };
-               DC00039919DBA70600023EB0 /* DFGPutLocalSinkingPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = DC00039719DBA70600023EB0 /* DFGPutLocalSinkingPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E124A8F70E555775003091F1 /* OpaqueJSString.h in Headers */ = {isa = PBXBuildFile; fileRef = E124A8F50E555775003091F1 /* OpaqueJSString.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E124A8F80E555775003091F1 /* OpaqueJSString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E124A8F60E555775003091F1 /* OpaqueJSString.cpp */; };
                E18E3A590DF9278C00D90B34 /* VM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E18E3A570DF9278C00D90B34 /* VM.cpp */; };
                0F38B01417CFE75500B144D3 /* DFGCompilationKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGCompilationKey.h; path = dfg/DFGCompilationKey.h; sourceTree = "<group>"; };
                0F38B01517CFE75500B144D3 /* DFGCompilationMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGCompilationMode.cpp; path = dfg/DFGCompilationMode.cpp; sourceTree = "<group>"; };
                0F38B01617CFE75500B144D3 /* DFGCompilationMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGCompilationMode.h; path = dfg/DFGCompilationMode.h; sourceTree = "<group>"; };
+               0F3A1BF71A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGPutStackSinkingPhase.cpp; path = dfg/DFGPutStackSinkingPhase.cpp; sourceTree = "<group>"; };
+               0F3A1BF81A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGPutStackSinkingPhase.h; path = dfg/DFGPutStackSinkingPhase.h; sourceTree = "<group>"; };
                0F3AC751183EA1040032029F /* StackAlignment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StackAlignment.h; sourceTree = "<group>"; };
                0F3AC753188E5EC80032029F /* ExitingJITType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExitingJITType.h; sourceTree = "<group>"; };
                0F3B3A17153E68EF003ED0FF /* DFGConstantFoldingPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGConstantFoldingPhase.cpp; path = dfg/DFGConstantFoldingPhase.cpp; sourceTree = "<group>"; };
                D21202280AD4310C00ED79B6 /* DateConversion.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DateConversion.cpp; sourceTree = "<group>"; };
                D21202290AD4310C00ED79B6 /* DateConversion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DateConversion.h; sourceTree = "<group>"; };
                DC00039019D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGPreciseLocalClobberize.h; path = dfg/DFGPreciseLocalClobberize.h; sourceTree = "<group>"; };
-               DC00039619DBA70600023EB0 /* DFGPutLocalSinkingPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGPutLocalSinkingPhase.cpp; path = dfg/DFGPutLocalSinkingPhase.cpp; sourceTree = "<group>"; };
-               DC00039719DBA70600023EB0 /* DFGPutLocalSinkingPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGPutLocalSinkingPhase.h; path = dfg/DFGPutLocalSinkingPhase.h; sourceTree = "<group>"; };
                E124A8F50E555775003091F1 /* OpaqueJSString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpaqueJSString.h; sourceTree = "<group>"; };
                E124A8F60E555775003091F1 /* OpaqueJSString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OpaqueJSString.cpp; sourceTree = "<group>"; };
                E178633F0D9BEC0000D74E75 /* InitializeThreading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InitializeThreading.h; sourceTree = "<group>"; };
                                0FAA3E0819D0C2CB00FAC9E2 /* DFGPromoteHeapAccess.h */,
                                0FB1765E196B8F9E0091052A /* DFGPureValue.cpp */,
                                0FB1765F196B8F9E0091052A /* DFGPureValue.h */,
-                               DC00039619DBA70600023EB0 /* DFGPutLocalSinkingPhase.cpp */,
-                               DC00039719DBA70600023EB0 /* DFGPutLocalSinkingPhase.h */,
+                               0F3A1BF71A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp */,
+                               0F3A1BF81A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h */,
                                86EC9DC11328DF82002B2AD7 /* DFGRegisterBank.h */,
                                0F666ECA1836B37E00D017F1 /* DFGResurrectionForValidationPhase.cpp */,
                                0F666ECB1836B37E00D017F1 /* DFGResurrectionForValidationPhase.h */,
                                BC18C4280E16F5CD00B34460 /* JSStringRef.h in Headers */,
                                BC18C4290E16F5CD00B34460 /* JSStringRefCF.h in Headers */,
                                1A28D4A8177B71C80007FA3C /* JSStringRefPrivate.h in Headers */,
-                               DC00039919DBA70600023EB0 /* DFGPutLocalSinkingPhase.h in Headers */,
                                0F919D0D157EE0A2004A4E7D /* JSSymbolTableObject.h in Headers */,
                                BC18C42A0E16F5CD00B34460 /* JSType.h in Headers */,
                                0F2B66FB17B6B5AB00A7AE3F /* JSTypedArrayConstructors.h in Headers */,
                                0FC8150A14043BF500CFA603 /* WriteBarrierSupport.h in Headers */,
                                9688CB160ED12B4E001D649F /* X86Assembler.h in Headers */,
                                A5840E2A187CA75900843B10 /* xxd.pl in Headers */,
+                               0F3A1BFA1A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h in Headers */,
                                451539B912DC994500EF7AC4 /* Yarr.h in Headers */,
                                86704B8512DBA33700A9FE7B /* YarrInterpreter.h in Headers */,
                                86704B8712DBA33700A9FE7B /* YarrJIT.h in Headers */,
                                A7D89CF517A0B8CC00773AD8 /* DFGCriticalEdgeBreakingPhase.cpp in Sources */,
                                0FFFC95914EF90A600C72532 /* DFGCSEPhase.cpp in Sources */,
                                0F2FC77216E12F710038D976 /* DFGDCEPhase.cpp in Sources */,
-                               DC00039819DBA70600023EB0 /* DFGPutLocalSinkingPhase.cpp in Sources */,
                                0F8F2B99172F04FF007DBDA5 /* DFGDesiredIdentifiers.cpp in Sources */,
                                C2C0F7CD17BBFC5B00464FE4 /* DFGDesiredTransitions.cpp in Sources */,
                                0FE8534B1723CDA500B618F5 /* DFGDesiredWatchpoints.cpp in Sources */,
                                0FD120331A8C85BD000F5280 /* FTLJSCallVarargs.cpp in Sources */,
                                0F9D339617FFC4E60073C2BC /* DFGFlushedAt.cpp in Sources */,
                                A7D89CF717A0B8CC00773AD8 /* DFGFlushFormat.cpp in Sources */,
+                               0F3A1BF91A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp in Sources */,
                                86EC9DC71328DF82002B2AD7 /* DFGGraph.cpp in Sources */,
                                0F2FCCF918A60070001A27F8 /* DFGGraphSafepoint.cpp in Sources */,
                                A704D90517A0BAA8006BA554 /* DFGInPlaceAbstractState.cpp in Sources */,
index 2bca9ae..1495c97 100644 (file)
@@ -167,6 +167,17 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
         
+    case GetStack: {
+        StackAccessData* data = node->stackAccessData();
+        AbstractValue value = m_state.variables().operand(data->local);
+        // The value in the local should already be checked.
+        DFG_ASSERT(m_graph, node, value.isType(typeFilterFor(data->format)));
+        if (value.value())
+            m_state.setFoundConstants(true);
+        forNode(node) = value;
+        break;
+    }
+        
     case GetLocalUnlinked: {
         AbstractValue value = m_state.variables().operand(node->unlinkedLocal().offset());
         if (value.value())
@@ -175,9 +186,13 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
         
-    case SetLocal:
-    case PutLocal: {
-        m_state.variables().operand(node->local().offset()) = forNode(node->child1());
+    case SetLocal: {
+        m_state.variables().operand(node->local()) = forNode(node->child1());
+        break;
+    }
+        
+    case PutStack: {
+        m_state.variables().operand(node->stackAccessData()->local) = forNode(node->child1());
         break;
     }
         
@@ -188,7 +203,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
         
-    case KillLocal: {
+    case KillStack: {
         // This is just a hint telling us that the OSR state of the local is no longer inside the
         // flushed data.
         break;
index b11ec92..286a533 100644 (file)
@@ -260,7 +260,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
          
     case MovHint:
     case ZombieHint:
-    case KillLocal:
+    case KillStack:
     case Upsilon:
     case Phi:
     case PhantomLocal:
@@ -406,11 +406,24 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         return;
         
     case SetLocal:
-    case PutLocal:
         write(AbstractHeap(Variables, node->local()));
         def(HeapLocation(VariableLoc, AbstractHeap(Variables, node->local())), node->child1().node());
         return;
         
+    case GetStack: {
+        AbstractHeap heap(Variables, node->stackAccessData()->local);
+        read(heap);
+        def(HeapLocation(VariableLoc, heap), node);
+        return;
+    }
+        
+    case PutStack: {
+        AbstractHeap heap(Variables, node->stackAccessData()->local);
+        write(heap);
+        def(HeapLocation(VariableLoc, heap), node->child1().node());
+        return;
+    }
+        
     case LoadVarargs:
         // This actually writes to local variables as well. But when it reads the array, it does
         // so in a way that may trigger getters or various traps.
index 95afd22..fa22ba7 100644 (file)
@@ -397,7 +397,10 @@ private:
                         m_insertionSet.insertNode(
                             indexInBlock, SpecNone, Phantom, node->origin, node->children);
                         
-                        node->convertToGetLocalUnlinked(VirtualRegister(operand));
+                        if (m_graph.m_form == SSA)
+                            node->convertToGetStack(m_graph.m_stackAccessData.add(VirtualRegister(operand), FlushedJSValue));
+                        else
+                            node->convertToGetLocalUnlinked(VirtualRegister(operand));
                         break;
                     }
                 }
index c6a44e4..c823ec7 100644 (file)
@@ -207,8 +207,9 @@ bool doesGC(Graph& graph, Node* node)
     case PutByOffsetHint:
     case CheckStructureImmediate:
     case PutStructureHint:
-    case PutLocal:
-    case KillLocal:
+    case PutStack:
+    case KillStack:
+    case GetStack:
         return false;
 
     case CreateActivation:
index e4675c2..a537455 100644 (file)
@@ -1058,8 +1058,9 @@ private:
         case CheckStructureImmediate:
         case PutStructureHint:
         case MaterializeNewObject:
-        case PutLocal:
-        case KillLocal:
+        case PutStack:
+        case KillStack:
+        case GetStack:
             // These are just nodes that we don't currently expect to see during fixup.
             // If we ever wanted to insert them prior to fixup, then we just have to create
             // fixup rules for them.
index b8a3426..268c1de 100644 (file)
@@ -134,6 +134,11 @@ inline FlushFormat merge(FlushFormat a, FlushFormat b)
     return ConflictingFlush;
 }
 
+inline bool isConcrete(FlushFormat format)
+{
+    return format != DeadFlush && format != ConflictingFlush;
+}
+
 } } // namespace JSC::DFG
 
 namespace WTF {
index 7c9f804..e7901d8 100644 (file)
@@ -282,6 +282,13 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node, DumpContext*
                 out.print(comma, "machine:", operand);
         }
     }
+    if (node->hasStackAccessData()) {
+        StackAccessData* data = node->stackAccessData();
+        out.print(comma, data->local);
+        if (data->machineLocal.isValid())
+            out.print(comma, "machine:", data->machineLocal);
+        out.print(comma, data->format);
+    }
     if (node->hasUnlinkedLocal()) 
         out.print(comma, node->unlinkedLocal());
     if (node->hasUnlinkedMachineLocal()) {
index ba0934c..01d1aa6 100644 (file)
@@ -783,7 +783,7 @@ public:
     // that survived DCE. All of them except maybe "this" will survive DCE, because of the Flush
     // nodes.
     //
-    // In SSA, this is all of the GetLocal nodes for the arguments in the machine code block that
+    // In SSA, this is all of the GetStack nodes for the arguments in the machine code block that
     // may have some speculation in the prologue and survived DCE. Note that to get the speculation
     // for an argument in SSA, you must use m_argumentFormats, since we still have to speculate
     // even if the argument got killed. For example:
@@ -794,7 +794,7 @@ public:
     //
     // Assume that x is always int during profiling. The ArithAdd for "x + 1" will be dead and will
     // have a proven check for the edge to "x". So, we will not insert a Check node and we will
-    // kill the GetLocal for "x". But, we must do the int check in the progolue, because that's the
+    // kill the GetStack for "x". But, we must do the int check in the progolue, because that's the
     // thing we used to allow DCE of ArithAdd. Otherwise the add could be impure:
     //
     //     var o = {
@@ -821,6 +821,7 @@ public:
     Bag<ObjectMaterializationData> m_objectMaterializationData;
     Bag<CallVarargsData> m_callVarargsData;
     Bag<LoadVarargsData> m_loadVarargsData;
+    Bag<StackAccessData> m_stackAccessData;
     Vector<InlineVariableData, 4> m_inlineVariableData;
     HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>> m_bytecodeLiveness;
     bool m_hasArguments;
index e77b7c3..c1eaa37 100644 (file)
@@ -79,8 +79,9 @@ bool mayExit(Graph& graph, Node* node)
     case PutStructureHint:
     case PutByOffsetHint:
     case PhantomNewObject:
-    case PutLocal:
-    case KillLocal:
+    case PutStack:
+    case KillStack:
+    case GetStack:
     case GetCallee:
     case GetScope:
     case PhantomLocal:
index 952c74c..b3022bb 100644 (file)
@@ -78,7 +78,6 @@ bool Node::hasVariableAccessData(Graph& graph)
     case SetArgument:
     case Flush:
     case PhantomLocal:
-    case PutLocal:
         return true;
     default:
         return false;
index 7528ee5..dafab2c 100644 (file)
@@ -197,6 +197,25 @@ struct LoadVarargsData {
     unsigned limit; // Maximum number of elements to load. Includes "this".
 };
 
+struct StackAccessData {
+    StackAccessData()
+        : format(DeadFlush)
+    {
+    }
+    
+    StackAccessData(VirtualRegister local, FlushFormat format)
+        : local(local)
+        , format(format)
+    {
+    }
+    
+    VirtualRegister local;
+    VirtualRegister machineLocal;
+    FlushFormat format;
+    
+    FlushedAt flushedAt() { return FlushedAt(format, machineLocal); }
+};
+
 // This type used in passing an immediate argument to Node constructor;
 // distinguishes an immediate value (typically an index into a CodeBlock data structure - 
 // a constant index, argument, or identifier) from a Node*.
@@ -484,6 +503,23 @@ struct Node {
         children.reset();
     }
     
+    void convertToPutStack(StackAccessData* data)
+    {
+        m_op = PutStack;
+        m_flags |= NodeMustGenerate;
+        m_opInfo = bitwise_cast<uintptr_t>(data);
+        m_opInfo2 = 0;
+    }
+    
+    void convertToGetStack(StackAccessData* data)
+    {
+        m_op = GetStack;
+        m_flags &= ~NodeMustGenerate;
+        m_opInfo = bitwise_cast<uintptr_t>(data);
+        m_opInfo2 = 0;
+        children.reset();
+    }
+    
     void convertToGetByOffset(StorageAccessData& data, Edge storage)
     {
         ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == MultiGetByOffset);
@@ -717,7 +753,7 @@ struct Node {
         case ExtractOSREntryLocal:
         case MovHint:
         case ZombieHint:
-        case KillLocal:
+        case KillStack:
             return true;
         default:
             return false;
@@ -747,6 +783,23 @@ struct Node {
         return VirtualRegister(m_opInfo2);
     }
     
+    bool hasStackAccessData()
+    {
+        switch (op()) {
+        case PutStack:
+        case GetStack:
+            return true;
+        default:
+            return false;
+        }
+    }
+    
+    StackAccessData* stackAccessData()
+    {
+        ASSERT(hasStackAccessData());
+        return bitwise_cast<StackAccessData*>(m_opInfo);
+    }
+    
     bool hasPhi()
     {
         return op() == Upsilon;
@@ -1428,8 +1481,6 @@ struct Node {
     {
         switch (op()) {
         case SetLocal:
-        case PutLocal:
-        case KillLocal:
         case MovHint:
         case ZombieHint:
         case PhantomArguments:
index 7735841..2c3cf04 100644 (file)
@@ -57,8 +57,11 @@ namespace JSC { namespace DFG {
     /* better names for a lot of these. https://bugs.webkit.org/show_bug.cgi?id=137307 */\
     macro(GetLocal, NodeResultJS) \
     macro(SetLocal, 0) \
-    macro(PutLocal, NodeMustGenerate) \
-    macro(KillLocal, NodeMustGenerate) \
+    \
+    macro(PutStack, NodeMustGenerate) \
+    macro(KillStack, NodeMustGenerate) \
+    macro(GetStack, NodeResultJS) \
+    \
     macro(MovHint, 0) \
     macro(ZombieHint, 0) \
     macro(Phantom, NodeMustGenerate) \
index 41c6d24..4537099 100644 (file)
@@ -128,21 +128,20 @@ void LocalOSRAvailabilityCalculator::endBlock(BasicBlock* block)
 void LocalOSRAvailabilityCalculator::executeNode(Node* node)
 {
     switch (node->op()) {
-    case PutLocal: {
-        VariableAccessData* variable = node->variableAccessData();
-        m_availability.m_locals.operand(variable->local()).setFlush(variable->flushedAt());
+    case PutStack: {
+        StackAccessData* data = node->stackAccessData();
+        m_availability.m_locals.operand(data->local).setFlush(data->flushedAt());
         break;
     }
         
-    case KillLocal: {
+    case KillStack: {
         m_availability.m_locals.operand(node->unlinkedLocal()).setFlush(FlushedAt(ConflictingFlush));
         break;
     }
 
-    case GetLocal: {
-        VariableAccessData* variable = node->variableAccessData();
-        m_availability.m_locals.operand(variable->local()) =
-            Availability(node, variable->flushedAt());
+    case GetStack: {
+        StackAccessData* data = node->stackAccessData();
+        m_availability.m_locals.operand(data->local) = Availability(node, data->flushedAt());
         break;
     }
 
index c46907a..f61fefc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -54,7 +54,7 @@
 #include "DFGPhantomRemovalPhase.h"
 #include "DFGPredictionInjectionPhase.h"
 #include "DFGPredictionPropagationPhase.h"
-#include "DFGPutLocalSinkingPhase.h"
+#include "DFGPutStackSinkingPhase.h"
 #include "DFGResurrectionForValidationPhase.h"
 #include "DFGSSAConversionPhase.h"
 #include "DFGSSALoweringPhase.h"
@@ -324,7 +324,7 @@ Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState)
         performCPSRethreading(dfg);
         performSSAConversion(dfg);
         performSSALowering(dfg);
-        performPutLocalSinking(dfg);
+        performPutStackSinking(dfg);
         performGlobalCSE(dfg);
         performLivenessAnalysis(dfg);
         performCFA(dfg);
index 9e3259d..f5c9ec4 100644 (file)
@@ -541,8 +541,9 @@ private:
         case CheckStructureImmediate:
         case PutStructureHint:
         case MaterializeNewObject:
-        case PutLocal:
-        case KillLocal: {
+        case PutStack:
+        case KillStack:
+        case GetStack: {
             // This node should never be visible at this stage of compilation. It is
             // inserted by fixup(), which follows this phase.
             RELEASE_ASSERT_NOT_REACHED();
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -24,7 +24,7 @@
  */
 
 #include "config.h"
-#include "DFGPutLocalSinkingPhase.h"
+#include "DFGPutStackSinkingPhase.h"
 
 #if ENABLE(DFG_JIT)
 
@@ -44,93 +44,28 @@ namespace {
 
 bool verbose = false;
 
-class VariableDeferral {
+class PutStackSinkingPhase : public Phase {
 public:
-    VariableDeferral(VariableAccessData* variable = nullptr)
-        : m_variable(variable)
-    {
-    }
-    
-    static VariableDeferral conflict()
-    {
-        return VariableDeferral(conflictMarker());
-    }
-    
-    bool operator!() const { return !m_variable; }
-    
-    bool hasVariable() const { return !!*this && !isConflict(); }
-    
-    VariableAccessData* variable() const
-    {
-        ASSERT(hasVariable());
-        return m_variable;
-    }
-    
-    bool isConflict() const
-    {
-        return m_variable == conflictMarker();
-    }
-    
-    VariableDeferral merge(VariableDeferral other) const
-    {
-        if (*this == other || !other)
-            return *this;
-        if (!*this)
-            return other;
-        return conflict();
-    }
-    
-    bool operator==(VariableDeferral other) const
-    {
-        return m_variable == other.m_variable;
-    }
-    
-    void dump(PrintStream& out) const
-    {
-        if (!*this)
-            out.print("-");
-        else if (isConflict())
-            out.print("Conflict");
-        else
-            out.print(RawPointer(m_variable));
-    }
-    
-    void dumpInContext(PrintStream& out, DumpContext*) const
-    {
-        dump(out);
-    }
-    
-private:
-    static VariableAccessData* conflictMarker()
-    {
-        return bitwise_cast<VariableAccessData*>(static_cast<intptr_t>(1));
-    }
-    
-    VariableAccessData* m_variable;
-};
-
-class PutLocalSinkingPhase : public Phase {
-public:
-    PutLocalSinkingPhase(Graph& graph)
-        : Phase(graph, "PutLocal sinking")
+    PutStackSinkingPhase(Graph& graph)
+        : Phase(graph, "PutStack sinking")
     {
     }
     
     bool run()
     {
         // FIXME: One of the problems of this approach is that it will create a duplicate Phi graph
-        // for sunken PutLocals in the presence of interesting control flow merges, and where the
-        // value being PutLocal'd is also otherwise live in the DFG code. We could work around this
+        // for sunken PutStacks in the presence of interesting control flow merges, and where the
+        // value being PutStack'd is also otherwise live in the DFG code. We could work around this
         // by doing the sinking over CPS, or maybe just by doing really smart hoisting. It's also
         // possible that the duplicate Phi graph can be deduplicated by LLVM. It would be best if we
         // could observe that there is already a Phi graph in place that does what we want. In
         // principle if we have a request to place a Phi at a particular place, we could just check
-        // if there is already a Phi that does what we want. Because PutLocalSinkingPhase runs just
+        // if there is already a Phi that does what we want. Because PutStackSinkingPhase runs just
         // after SSA conversion, we have almost a guarantee that the Phi graph we produce here would
         // be trivially redundant to the one we already have.
         
         if (verbose) {
-            dataLog("Graph before PutLocal sinking:\n");
+            dataLog("Graph before PutStack sinking:\n");
             m_graph.dump();
         }
         
@@ -180,7 +115,7 @@ public:
                                 return;
                             }
                             
-                            RELEASE_ASSERT(node->op() == PutLocal);
+                            RELEASE_ASSERT(node->op() == PutStack);
                             live.operand(operand) = false;
                         });
                 }
@@ -212,76 +147,77 @@ public:
         for (size_t i = liveAtHead.atIndex(0).numberOfArguments(); i--;)
             DFG_ASSERT(m_graph, nullptr, liveAtHead.atIndex(0).argument(i));
         
-        // Next identify where we would want to sink PutLocals to. We say that there is a deferred
-        // flush if we had a PutLocal with a given VariableAccessData* but it hasn't been
-        // materialized yet. Deferrals have the following lattice; but it's worth noting that the
-        // TOP part of the lattice serves an entirely different purpose than the rest of the lattice:
-        // it just means that we're in a region of code where nobody should have been relying on the
-        // value. The rest of the lattice means that we either have a PutLocal that is deferred (i.e.
-        // still needs to be executed) or there isn't one (because we've alraedy executed it).
+        // Next identify where we would want to sink PutStacks to. We say that there is a deferred
+        // flush if we had a PutStack with a given FlushFormat but it hasn't been materialized yet.
+        // Deferrals have the following lattice; but it's worth noting that the TOP part of the
+        // lattice serves an entirely different purpose than the rest of the lattice: it just means
+        // that we're in a region of code where nobody should have been relying on the value. The
+        // rest of the lattice means that we either have a PutStack that is deferred (i.e. still
+        // needs to be executed) or there isn't one (because we've alraedy executed it).
         //
         // Bottom:
-        //     Instantiated as VariableDeferral()
-        //     Means that all previous PutLocals have been executed so there is nothing deferred.
+        //     Represented as DeadFlush
+        //     Means that all previous PutStacks have been executed so there is nothing deferred.
         //     During merging this is subordinate to the other kinds of deferrals, because it
-        //     represents the fact that we've already executed all necessary PutLocals. This implies
-        //     that there *had* been some PutLocals that we should have executed.
+        //     represents the fact that we've already executed all necessary PutStacks. This implies
+        //     that there *had* been some PutStacks that we should have executed.
         //
         // Top:
-        //     Instantiated as VariableDeferral::conflict().
+        //     Represented as ConflictingFlush.
         //     Represents the fact that we know, via forward flow, that there isn't any value in the
         //     given local that anyone should have been relying on. This comes into play at the
         //     prologue (because in SSA form at the prologue no local has any value) or when we merge
-        //     deferrals for different VariableAccessData*'s. A VAD encompasses a lexical scope in
-        //     which the local has some semantic meaning; if we had stores from different lexical
-        //     scopes that got merged together then we know that we're not in either scope anymore.
-        //     Note that this is all approximate and only precise enough to later answer questions
-        //     pertinent to sinking. For example, this doesn't always detect when a local is no
-        //     longer semantically relevant - we may well have a deferral from inside some inlined
-        //     call survive outside of that inlined code, and this is generally OK. In the worst case
-        //     it means that we might think that a deferral that is actually dead must still be
-        //     executed. But we usually catch that with liveness. Liveness doesn't always catch it
-        //     because liveness is conservative.
+        //     deferrals for different formats's. A lexical scope in which a local had some semantic
+        //     meaning will by this point share the same format; if we had stores from different
+        //     lexical scopes that got merged together then we may have a conflicting format. Hence
+        //     a conflicting format proves that we're no longer in an area in which the variable was
+        //     in scope. Note that this is all approximate and only precise enough to later answer
+        //     questions pertinent to sinking. For example, this doesn't always detect when a local
+        //     is no longer semantically relevant - we may well have a deferral from inside some
+        //     inlined call survive outside of that inlined code, and this is generally OK. In the
+        //     worst case it means that we might think that a deferral that is actually dead must
+        //     still be executed. But we usually catch that with liveness. Liveness usually catches
+        //     such cases, but that's not guaranteed since liveness is conservative.
         //
         //     What Top does give us is detects situations where we both don't need to care about a
         //     deferral and there is no way that we could reason about it anyway. If we merged
-        //     deferrals for different variables then we wouldn't know the format to use. So, we
-        //     use Top in that case because that's also a case where we know that we can ignore the
+        //     deferrals for different formats then we wouldn't know the format to use. So, we use
+        //     Top in that case because that's also a case where we know that we can ignore the
         //     deferral.
         //
-        // Deferral with a concrete VariableAccessData*:
-        //     Instantiated as VariableDeferral(someVariableAccessData)
-        //     Represents the fact that the original code would have done a PutLocal but we haven't
-        //     identified an operation that would have observed that PutLocal.
+        // Deferral with a concrete format:
+        //     Represented by format values other than DeadFlush or ConflictingFlush.
+        //     Represents the fact that the original code would have done a PutStack but we haven't
+        //     identified an operation that would have observed that PutStack.
         //
         // This code has some interesting quirks because of the fact that neither liveness nor
         // deferrals are very precise. They are only precise enough to be able to correctly tell us
-        // when we may [sic] need to execute PutLocals. This means that they may report the need to
-        // execute a PutLocal in cases where we actually don't really need it, and that's totally OK.
-        BlockMap<Operands<VariableDeferral>> deferredAtHead(m_graph);
-        BlockMap<Operands<VariableDeferral>> deferredAtTail(m_graph);
+        // when we may [sic] need to execute PutStacks. This means that they may report the need to
+        // execute a PutStack in cases where we actually don't really need it, and that's totally OK.
+        BlockMap<Operands<FlushFormat>> deferredAtHead(m_graph);
+        BlockMap<Operands<FlushFormat>> deferredAtTail(m_graph);
         
         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
             deferredAtHead[block] =
-                Operands<VariableDeferral>(OperandsLike, block->variablesAtHead);
+                Operands<FlushFormat>(OperandsLike, block->variablesAtHead);
             deferredAtTail[block] =
-                Operands<VariableDeferral>(OperandsLike, block->variablesAtHead);
+                Operands<FlushFormat>(OperandsLike, block->variablesAtHead);
         }
         
-        deferredAtHead.atIndex(0).fill(VariableDeferral::conflict());
+        deferredAtHead.atIndex(0).fill(ConflictingFlush);
         
         do {
             changed = false;
             
             for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
-                Operands<VariableDeferral> deferred = deferredAtHead[block];
+                Operands<FlushFormat> deferred = deferredAtHead[block];
                 
                 for (Node* node : *block) {
                     if (verbose)
                         dataLog("Deferred at ", node, ":", deferred, "\n");
                     
-                    if (node->op() == KillLocal) {
-                        deferred.operand(node->unlinkedLocal()) = VariableDeferral::conflict();
+                    if (node->op() == KillStack) {
+                        deferred.operand(node->unlinkedLocal()) = ConflictingFlush;
                         continue;
                     }
                     
@@ -289,7 +225,7 @@ public:
                         if (operand.isHeader())
                             return;
                         // We will materialize just before any reads.
-                        deferred.operand(operand) = VariableDeferral();
+                        deferred.operand(operand) = DeadFlush;
                     };
                     
                     preciseLocalClobberize(
@@ -300,7 +236,7 @@ public:
                                 return;
                             }
                             
-                            deferred.operand(operand) = VariableDeferral(node->variableAccessData());
+                            deferred.operand(operand) = node->stackAccessData()->format;
                         });
                 }
                 
@@ -316,7 +252,7 @@ public:
                             dataLog("Considering ", VirtualRegister(deferred.operandForIndex(i)), " at ", pointerDump(block), "->", pointerDump(successor), ": ", deferred[i], " and ", deferredAtHead[successor][i], " merges to ");
 
                         deferredAtHead[successor][i] =
-                            deferredAtHead[successor][i].merge(deferred[i]);
+                            merge(deferredAtHead[successor][i], deferred[i]);
                         
                         if (verbose)
                             dataLog(deferredAtHead[successor][i], "\n");
@@ -326,16 +262,16 @@ public:
             
         } while (changed);
         
-        // We wish to insert PutLocals at all of the materialization points, which are defined
+        // We wish to insert PutStacks at all of the materialization points, which are defined
         // implicitly as the places where we set deferred to Dead while it was previously not Dead.
         // To do this, we may need to build some Phi functions to handle stuff like this:
         //
         // Before:
         //
         //     if (p)
-        //         PutLocal(r42, @x)
+        //         PutStack(r42, @x)
         //     else
-        //         PutLocal(r42, @y)
+        //         PutStack(r42, @y)
         //
         // After:
         //
@@ -344,10 +280,10 @@ public:
         //     else
         //         Upsilon(@y, ^z)
         //     z: Phi()
-        //     PutLocal(r42, @z)
+        //     PutStack(r42, @z)
         //
         // This means that we have an SSACalculator::Variable for each local, and a Def is any
-        // PutLocal in the original program. The original PutLocals will simply vanish.
+        // PutStack in the original program. The original PutStacks will simply vanish.
         
         Operands<SSACalculator::Variable*> operandToVariable(
             OperandsLike, m_graph.block(0)->variablesAtHead);
@@ -366,14 +302,16 @@ public:
         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
             for (Node* node : *block) {
                 switch (node->op()) {
-                case PutLocal:
+                case PutStack:
                     putLocalsToSink.add(node);
                     ssaCalculator.newDef(
-                        operandToVariable.operand(node->local()), block, node->child1().node());
+                        operandToVariable.operand(node->stackAccessData()->local),
+                        block, node->child1().node());
                     break;
-                case GetLocal:
+                case GetStack:
                     ssaCalculator.newDef(
-                        operandToVariable.operand(node->local()), block, node);
+                        operandToVariable.operand(node->stackAccessData()->local),
+                        block, node);
                     break;
                 default:
                     break;
@@ -388,24 +326,22 @@ public:
                 if (!liveAtHead[block].operand(operand))
                     return nullptr;
                 
-                VariableDeferral variableDeferral = deferredAtHead[block].operand(operand);
+                FlushFormat format = deferredAtHead[block].operand(operand);
 
                 // We could have an invalid deferral because liveness is imprecise.
-                if (!variableDeferral.hasVariable())
+                if (!isConcrete(format))
                     return nullptr;
 
                 if (verbose)
                     dataLog("Adding Phi for ", operand, " at ", pointerDump(block), "\n");
                 
                 Node* phiNode = m_graph.addNode(SpecHeapTop, Phi, NodeOrigin());
-                DFG_ASSERT(m_graph, nullptr, variableDeferral.hasVariable());
-                FlushFormat format = variableDeferral.variable()->flushFormat();
                 phiNode->mergeFlags(resultFor(format));
                 return phiNode;
             });
         
         Operands<Node*> mapping(OperandsLike, m_graph.block(0)->variablesAtHead);
-        Operands<VariableDeferral> deferred;
+        Operands<FlushFormat> deferred;
         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
             mapping.fill(nullptr);
             
@@ -440,18 +376,18 @@ public:
                     dataLog("Deferred at ", node, ":", deferred, "\n");
                 
                 switch (node->op()) {
-                case PutLocal: {
-                    VariableAccessData* variable = node->variableAccessData();
-                    VirtualRegister operand = variable->local();
-                    deferred.operand(operand) = VariableDeferral(variable);
+                case PutStack: {
+                    StackAccessData* data = node->stackAccessData();
+                    VirtualRegister operand = data->local;
+                    deferred.operand(operand) = data->format;
                     if (verbose)
                         dataLog("   Mapping ", operand, " to ", node->child1().node(), " at ", node, "\n");
                     mapping.operand(operand) = node->child1().node();
                     break;
                 }
                     
-                case KillLocal: {
-                    deferred.operand(node->unlinkedLocal()) = VariableDeferral();
+                case KillStack: {
+                    deferred.operand(node->unlinkedLocal()) = ConflictingFlush;
                     break;
                 }
                 
@@ -460,36 +396,36 @@ public:
                         if (operand.isHeader())
                             return;
                     
-                        VariableDeferral variableDeferral = deferred.operand(operand);
-                        if (!variableDeferral.hasVariable())
+                        FlushFormat format = deferred.operand(operand);
+                        if (!isConcrete(format))
                             return;
                     
-                        // Gotta insert a PutLocal.
+                        // Gotta insert a PutStack.
                         if (verbose)
-                            dataLog("Inserting a PutLocal for ", operand, " at ", node, "\n");
+                            dataLog("Inserting a PutStack for ", operand, " at ", node, "\n");
 
                         Node* incoming = mapping.operand(operand);
                         DFG_ASSERT(m_graph, node, incoming);
                     
                         insertionSet.insertNode(
-                            nodeIndex, SpecNone, PutLocal, node->origin,
-                            OpInfo(variableDeferral.variable()),
-                            Edge(incoming, useKindFor(variableDeferral.variable()->flushFormat())));
+                            nodeIndex, SpecNone, PutStack, node->origin,
+                            OpInfo(m_graph.m_stackAccessData.add(operand, format)),
+                            Edge(incoming, useKindFor(format)));
                     
-                        deferred.operand(operand) = nullptr;
+                        deferred.operand(operand) = DeadFlush;
                     };
                 
                     preciseLocalClobberize(
                         m_graph, node, escapeHandler, escapeHandler,
                         [&] (VirtualRegister, Node*) { });
                     
-                    // If we're a GetLocal, then we also create a mapping.
+                    // If we're a GetStack, then we also create a mapping.
                     // FIXME: We should be able to just eliminate such GetLocals, when we know
                     // what their incoming value will be.
                     // https://bugs.webkit.org/show_bug.cgi?id=141624
-                    if (node->op() == GetLocal) {
-                        VariableAccessData* variable = node->variableAccessData();
-                        VirtualRegister operand = variable->local();
+                    if (node->op() == GetStack) {
+                        StackAccessData* data = node->stackAccessData();
+                        VirtualRegister operand = data->local;
                         mapping.operand(operand) = node;
                     }
                     break;
@@ -505,10 +441,8 @@ public:
                     VirtualRegister operand = indexToOperand[variable->index()];
                     if (verbose)
                         dataLog("Creating Upsilon for ", operand, " at ", pointerDump(block), "->", pointerDump(successorBlock), "\n");
-                    VariableDeferral variableDeferral =
-                        deferredAtHead[successorBlock].operand(operand);
-                    DFG_ASSERT(m_graph, nullptr, variableDeferral.hasVariable());
-                    FlushFormat format = variableDeferral.variable()->flushFormat();
+                    FlushFormat format = deferredAtHead[successorBlock].operand(operand);
+                    DFG_ASSERT(m_graph, nullptr, isConcrete(format));
                     UseKind useKind = useKindFor(format);
                     Node* incoming = mapping.operand(operand);
                     if (!incoming) {
@@ -524,12 +458,12 @@ public:
                         // if (predicate1)
                         //     PutClosureVar(loc42) // prevent GCSE of our GetClosureVar's
                         // if (predicate2)
-                        //     PutLocal(loc42) // we now have a concrete deferral
+                        //     PutStack(loc42) // we now have a concrete deferral
                         // // we still have the concrete deferral because we merged with bottom
                         // GetClosureVar(loc42) // force materialization
                         //
                         // We will have a Phi with no incoming value form the basic block that
-                        // bypassed the PutLocal.
+                        // bypassed the PutStack.
                         
                         // Note: we sort of could have used the equivalent of LLVM's undef here. The
                         // point is that it's OK to just leave random bits in the local if we're
@@ -549,7 +483,7 @@ public:
             insertionSet.execute(block);
         }
         
-        // Finally eliminate the sunken PutLocals by turning them into Phantoms. This keeps whatever
+        // Finally eliminate the sunken PutStacks by turning them into Phantoms. This keeps whatever
         // type check they were doing. Also prepend KillLocals to them to ensure that we know that
         // the relevant value was *not* stored to the stack.
         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
@@ -560,7 +494,7 @@ public:
                     continue;
                 
                 insertionSet.insertNode(
-                    nodeIndex, SpecNone, KillLocal, node->origin, OpInfo(node->local().offset()));
+                    nodeIndex, SpecNone, KillStack, node->origin, OpInfo(node->stackAccessData()->local.offset()));
                 node->convertToPhantom();
             }
             
@@ -568,7 +502,7 @@ public:
         }
         
         if (verbose) {
-            dataLog("Graph after PutLocal sinking:\n");
+            dataLog("Graph after PutStack sinking:\n");
             m_graph.dump();
         }
         
@@ -578,10 +512,10 @@ public:
 
 } // anonymous namespace
     
-bool performPutLocalSinking(Graph& graph)
+bool performPutStackSinking(Graph& graph)
 {
-    SamplingRegion samplingRegion("DFG PutLocal Sinking Phase");
-    return runPhase<PutLocalSinkingPhase>(graph);
+    SamplingRegion samplingRegion("DFG PutStack Sinking Phase");
+    return runPhase<PutStackSinkingPhase>(graph);
 }
 
 } } // namespace JSC::DFG
@@ -1,5 +1,5 @@
  /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -23,8 +23,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#ifndef DFGPutLocalSinkingPhase_h
-#define DFGPutLocalSinkingPhase_h
+#ifndef DFGPutStackSinkingPhase_h
+#define DFGPutStackSinkingPhase_h
 
 #if ENABLE(DFG_JIT)
 
@@ -32,15 +32,15 @@ namespace JSC { namespace DFG {
 
 class Graph;
 
-// Sinks PutLocals to the absolute latest point where they can possibly happen, which is usually
-// side-effects that may observe them. This eliminates PutLocals if it sinks them past the point of
+// Sinks PutStacks to the absolute latest point where they can possibly happen, which is usually
+// side-effects that may observe them. This eliminates PutStacks if it sinks them past the point of
 // their deaths.
 
-bool performPutLocalSinking(Graph&);
+bool performPutStackSinking(Graph&);
 
 } } // namespace JSC::DFG
 
 #endif // ENABLE(DFG_JIT)
 
-#endif // DFGPutLocalSinkingPhase_h
+#endif // DFGPutStackSinkingPhase_h
 
index cc0a06b..632a378 100644 (file)
@@ -97,8 +97,10 @@ public:
                     ASSERT(node->op() == SetArgument);
                     childNode = m_insertionSet.insertNode(
                         nodeIndex, node->variableAccessData()->prediction(),
-                        GetLocal, node->origin, OpInfo(node->variableAccessData()));
-                    m_argumentGetters.add(childNode);
+                        GetStack, node->origin,
+                        OpInfo(m_graph.m_stackAccessData.add(variable->local(), variable->flushFormat())));
+                    if (!ASSERT_DISABLED)
+                        m_argumentGetters.add(childNode);
                     m_argumentMapping.add(node, childNode);
                 }
                 
@@ -188,26 +190,26 @@ public:
         //
         //   - MovHint has KillLocal prepended to it.
         //
-        //   - GetLocal over captured variables lose their phis.
+        //   - GetLocal over captured variables lose their phis and become GetStack.
         //
         //   - GetLocal over uncaptured variables die and get replaced with references to the node
         //     specified by valueForOperand.
         //
-        //   - SetLocal turns into PutLocal if it's flushed, or turns into a Check otherwise.
+        //   - SetLocal turns into PutStack if it's flushed, or turns into a Check otherwise.
         //
         //   - Flush loses its children and turns into a Phantom.
         //
         //   - PhantomLocal becomes Phantom, and its child is whatever is specified by
         //     valueForOperand.
         //
-        //   - SetArgument is removed. Note that GetLocal nodes have already been inserted.
+        //   - SetArgument is removed. Note that GetStack nodes have already been inserted.
         Operands<Node*> valueForOperand(OperandsLike, m_graph.block(0)->variablesAtHead);
         for (BasicBlock* block : m_graph.blocksInPreOrder()) {
             valueForOperand.clear();
             
             // CPS will claim that the root block has all arguments live. But we have already done
             // the first step of SSA conversion: argument locals are no longer live at head;
-            // instead we have GetLocal nodes for extracting the values of arguments. So, we
+            // instead we have GetStack nodes for extracting the values of arguments. So, we
             // skip the at-head available value calculation for the root block.
             if (block != m_graph.block(0)) {
                 for (size_t i = valueForOperand.size(); i--;) {
@@ -273,7 +275,7 @@ public:
                 switch (node->op()) {
                 case MovHint: {
                     m_insertionSet.insertNode(
-                        nodeIndex, SpecNone, KillLocal, node->origin,
+                        nodeIndex, SpecNone, KillStack, node->origin,
                         OpInfo(node->unlinkedLocal().offset()));
                     break;
                 }
@@ -281,9 +283,11 @@ public:
                 case SetLocal: {
                     VariableAccessData* variable = node->variableAccessData();
                     
-                    if (variable->isCaptured() || !!(node->flags() & NodeIsFlushed))
-                        node->setOpAndDefaultFlags(PutLocal);
-                    else
+                    if (variable->isCaptured() || !!(node->flags() & NodeIsFlushed)) {
+                        node->convertToPutStack(
+                            m_graph.m_stackAccessData.add(
+                                variable->local(), variable->flushFormat()));
+                    } else
                         node->setOpAndDefaultFlags(Check);
                     
                     if (!variable->isCaptured()) {
@@ -294,18 +298,20 @@ public:
                     break;
                 }
                     
+                case GetStack: {
+                    ASSERT(m_argumentGetters.contains(node));
+                    valueForOperand.operand(node->stackAccessData()->local) = node;
+                    break;
+                }
+                    
                 case GetLocal: {
                     VariableAccessData* variable = node->variableAccessData();
-                    if (m_argumentGetters.contains(node)) {
-                        if (verbose)
-                            dataLog("Mapping: ", variable->local(), " -> ", node, "\n");
-                        valueForOperand.operand(variable->local()) = node;
-                        break;
-                    }
                     node->children.reset();
                     
-                    if (variable->isCaptured())
+                    if (variable->isCaptured()) {
+                        node->convertToGetStack(m_graph.m_stackAccessData.add(variable->local(), variable->flushFormat()));
                         break;
+                    }
                     
                     node->convertToPhantom();
                     if (verbose)
@@ -401,7 +407,7 @@ public:
             // track the argument loads of those arguments for which we speculate type. We don't
             // speculate type for captured arguments.
             if (node)
-                format = node->variableAccessData()->flushFormat();
+                format = node->stackAccessData()->format;
             
             m_graph.m_argumentFormats[i] = format;
             m_graph.m_arguments[i] = node; // Record the load that loads the arguments for the benefit of exit profiling.
index 0da2ff2..512ddd7 100644 (file)
@@ -118,8 +118,9 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case GetCallee:
     case GetLocal:
     case SetLocal:
-    case PutLocal:
-    case KillLocal:
+    case PutStack:
+    case KillStack:
+    case GetStack:
     case MovHint:
     case ZombieHint:
     case Phantom:
index bd0a198..648dd0a 100644 (file)
@@ -5088,8 +5088,9 @@ void SpeculativeJIT::compile(Node* node)
     case CheckStructureImmediate:
     case PutStructureHint:
     case MaterializeNewObject:
-    case PutLocal:
-    case KillLocal:
+    case PutStack:
+    case KillStack:
+    case GetStack:
         RELEASE_ASSERT_NOT_REACHED();
         break;
     }
index a214e36..d48fce1 100644 (file)
@@ -5159,8 +5159,9 @@ void SpeculativeJIT::compile(Node* node)
     case CheckStructureImmediate:
     case PutStructureHint:
     case MaterializeNewObject:
-    case PutLocal:
-    case KillLocal:
+    case PutStack:
+    case KillStack:
+    case GetStack:
         DFG_CRASH(m_jit.graph(), node, "Unexpected node");
         break;
     }
index 90f0de1..7221f2d 100644 (file)
@@ -66,7 +66,6 @@ public:
                 switch (node->op()) {
                 case GetLocal:
                 case SetLocal:
-                case PutLocal:
                 case Flush:
                 case PhantomLocal: {
                     VariableAccessData* variable = node->variableAccessData();
@@ -100,6 +99,15 @@ public:
                     break;
                 }
                     
+                case PutStack:
+                case GetStack: {
+                    StackAccessData* stack = node->stackAccessData();
+                    if (stack->local.isArgument())
+                        break;
+                    usedLocals.set(stack->local.toLocal());
+                    break;
+                }
+                    
                 default:
                     break;
                 }
@@ -171,6 +179,20 @@ public:
             variable->machineLocal() = assign(allocation, variable->local());
         }
         
+        for (StackAccessData* data : m_graph.m_stackAccessData) {
+            if (!data->local.isLocal()) {
+                data->machineLocal = data->local;
+                continue;
+            }
+            
+            if (static_cast<size_t>(data->local.toLocal()) >= allocation.size())
+                continue;
+            if (allocation[data->local.toLocal()] == UINT_MAX)
+                continue;
+            
+            data->machineLocal = assign(allocation, data->local);
+        }
+        
         if (codeBlock()->usesArguments()) {
             VirtualRegister argumentsRegister =
                 assign(allocation, codeBlock()->argumentsRegister());
index 56eb756..329a076 100644 (file)
@@ -214,7 +214,7 @@ public:
                     VALIDATE((node), canonicalResultRepresentation(node->result()) == canonicalResultRepresentation(node->child1()->result()));
                     break;
                 case SetLocal:
-                case PutLocal:
+                case PutStack:
                 case Upsilon:
                     VALIDATE((node), !!node->child1());
                     switch (node->child1().useKind()) {
@@ -433,6 +433,24 @@ private:
                     }
                 }
                 
+                switch (node->op()) {
+                case Phi:
+                case Upsilon:
+                case CheckInBounds:
+                case PhantomNewObject:
+                case PutByOffsetHint:
+                case CheckStructureImmediate:
+                case PutStructureHint:
+                case MaterializeNewObject:
+                case PutStack:
+                case KillStack:
+                case GetStack:
+                    VALIDATE((node), !"unexpected node type in CPS");
+                    break;
+                default:
+                    break;
+                }
+                
                 if (!node->shouldGenerate())
                     continue;
                 switch (node->op()) {
@@ -493,6 +511,8 @@ private:
             if (!block)
                 continue;
             
+            VALIDATE((block), block->phis.isEmpty());
+            
             unsigned nodeIndex = 0;
             for (; nodeIndex < block->size() && !block->at(nodeIndex)->origin.forExit.isSet(); nodeIndex++) { }
             
@@ -508,6 +528,13 @@ private:
                     VALIDATE((node), !node->origin.forExit.isSet());
                     break;
                     
+                case GetLocal:
+                case SetLocal:
+                case GetLocalUnlinked:
+                case SetArgument:
+                    VALIDATE((node), !"bad node type for SSA");
+                    break;
+                    
                 default:
                     // FIXME: Add more things here.
                     // https://bugs.webkit.org/show_bug.cgi?id=123471
index 29310d3..dba9f12 100644 (file)
@@ -45,6 +45,8 @@ public:
     
     bool run()
     {
+        DFG_ASSERT(m_graph, nullptr, m_graph.m_form == ThreadedCPS);
+        
         ScoreBoard scoreBoard(m_graph.m_nextMachineLocal);
         scoreBoard.assertClear();
         for (size_t blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
index 2ebec33..e54e396 100644 (file)
@@ -47,8 +47,9 @@ inline CapabilityLevel canCompile(Node* node)
     case GetMyArgumentsLength:
     case GetLocal:
     case SetLocal:
-    case PutLocal:
-    case KillLocal:
+    case PutStack:
+    case KillStack:
+    case GetStack:
     case MovHint:
     case ZombieHint:
     case Phantom:
index f4e68d6..2d6dadc 100644 (file)
@@ -253,7 +253,7 @@ public:
             LValue jsValue = m_out.load64(addressFor(operand));
             
             if (node) {
-                DFG_ASSERT(m_graph, node, operand == node->variableAccessData()->machineLocal());
+                DFG_ASSERT(m_graph, node, operand == node->stackAccessData()->machineLocal);
                 
                 // This is a hack, but it's an effective one. It allows us to do CSE on the
                 // primordial load of arguments. This assumes that the GetLocal that got put in
@@ -455,11 +455,11 @@ private:
         case ExtractOSREntryLocal:
             compileExtractOSREntryLocal();
             break;
-        case GetLocal:
-            compileGetLocal();
+        case GetStack:
+            compileGetStack();
             break;
-        case PutLocal:
-            compilePutLocal();
+        case PutStack:
+            compilePutStack();
             break;
         case GetMyArgumentsLength:
             compileGetMyArgumentsLength();
@@ -828,7 +828,7 @@ private:
         case PutByOffsetHint:
         case PutStructureHint:
         case BottomValue:
-        case KillLocal:
+        case KillStack:
             break;
         default:
             DFG_CRASH(m_graph, m_node, "Unrecognized node in FTL backend");
@@ -1067,7 +1067,7 @@ private:
         setJSValue(m_out.load64(m_out.absolute(buffer + m_node->unlinkedLocal().toLocal())));
     }
     
-    void compileGetLocal()
+    void compileGetStack()
     {
         // GetLocals arise only for captured variables and arguments. For arguments, we might have
         // already loaded it.
@@ -1076,47 +1076,50 @@ private:
             return;
         }
         
-        VariableAccessData* variable = m_node->variableAccessData();
-        AbstractValue& value = m_state.variables().operand(variable->local());
+        StackAccessData* data = m_node->stackAccessData();
+        AbstractValue& value = m_state.variables().operand(data->local);
+        
+        DFG_ASSERT(m_graph, m_node, isConcrete(data->format));
+        DFG_ASSERT(m_graph, m_node, data->format != FlushedDouble); // This just happens to not arise for GetStacks, right now. It would be trivial to support.
         
         if (isInt32Speculation(value.m_type))
-            setInt32(m_out.load32(payloadFor(variable->machineLocal())));
+            setInt32(m_out.load32(payloadFor(data->machineLocal)));
         else
-            setJSValue(m_out.load64(addressFor(variable->machineLocal())));
+            setJSValue(m_out.load64(addressFor(data->machineLocal)));
     }
     
-    void compilePutLocal()
+    void compilePutStack()
     {
-        VariableAccessData* variable = m_node->variableAccessData();
-        switch (variable->flushFormat()) {
+        StackAccessData* data = m_node->stackAccessData();
+        switch (data->format) {
         case FlushedJSValue:
         case FlushedArguments: {
             LValue value = lowJSValue(m_node->child1());
-            m_out.store64(value, addressFor(variable->machineLocal()));
+            m_out.store64(value, addressFor(data->machineLocal));
             break;
         }
             
         case FlushedDouble: {
             LValue value = lowDouble(m_node->child1());
-            m_out.storeDouble(value, addressFor(variable->machineLocal()));
+            m_out.storeDouble(value, addressFor(data->machineLocal));
             break;
         }
             
         case FlushedInt32: {
             LValue value = lowInt32(m_node->child1());
-            m_out.store32(value, payloadFor(variable->machineLocal()));
+            m_out.store32(value, payloadFor(data->machineLocal));
             break;
         }
             
         case FlushedInt52: {
             LValue value = lowInt52(m_node->child1());
-            m_out.store64(value, addressFor(variable->machineLocal()));
+            m_out.store64(value, addressFor(data->machineLocal));
             break;
         }
             
         case FlushedCell: {
             LValue value = lowCell(m_node->child1());
-            m_out.store64(value, addressFor(variable->machineLocal()));
+            m_out.store64(value, addressFor(data->machineLocal));
             break;
         }
             
@@ -1124,7 +1127,7 @@ private:
             speculateBoolean(m_node->child1());
             m_out.store64(
                 lowJSValue(m_node->child1(), ManualOperandSpeculation),
-                addressFor(variable->machineLocal()));
+                addressFor(data->machineLocal));
             break;
         }
             
index 4e261a7..a2d28f6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -45,14 +45,12 @@ namespace JSC { namespace FTL {
 
 // Tracks one OSR exit site within the FTL JIT. OSR exit in FTL works by deconstructing
 // the crazy that is OSR down to simple SSA CFG primitives that any compiler backend
-// (including of course LLVM) can grok and do meaningful things to. Except for
-// watchpoint-based exits, which haven't yet been implemented (see webkit.org/b/113647),
-// an exit is just a conditional branch in the emitted code where one destination is the
-// continuation and the other is a basic block that performs a no-return tail-call to an
-// exit thunk. This thunk takes as its arguments the live non-constant
-// not-already-accounted-for bytecode state. To appreciate how this works consider the
-// following JavaScript program, and its lowering down to LLVM IR including the relevant
-// exits:
+// (including of course LLVM) can grok and do meaningful things to. An exit is just a
+// conditional branch in the emitted code where one destination is the continuation and
+// the other is a basic block that performs a no-return tail-call to an  exit thunk.
+// This thunk takes as its arguments the live non-constant not-already-accounted-for
+// bytecode state. To appreciate how this works consider the following JavaScript
+// program, and its lowering down to LLVM IR including the relevant exits:
 //
 // function foo(o) {
 //     var a = o.a; // predicted int
@@ -66,10 +64,10 @@ namespace JSC { namespace FTL {
 //
 // BitOr(Check:Int32:@a, Int32:5)
 //
-// Where @a is the node for the GetLocal node that gets the value of the 'a' variable.
-// Conceptually, this node can be further broken down to the following (note that this
-// particular lowering never actually happens - we skip this step and go straight to
-// LLVM IR - but it's still useful to see this):
+// Where @a is the node for the value of the 'a' variable. Conceptually, this node can
+// be further broken down to the following (note that this particular lowering never
+// actually happens - we skip this step and go straight to LLVM IR - but it's still
+// useful to see this):
 //
 // exitIf(@a is not int32);
 // continuation;
@@ -124,22 +122,12 @@ namespace JSC { namespace FTL {
 //   arguments into argument position), the backend could choose to simply inform us
 //   where it had placed the arguments and expect the callee (i.e. the exit thunk) to
 //   figure it out from there. It could also tell us what we need to do to pop stack,
-//   although again, it doesn't have to; it could just emit that code normally. Though
-//   we don't support this yet, we could; the only thing that would change on our end
-//   is that we'd need feedback from the backend about the location of the arguments
-//   and a description of the things that need to be done to pop stack. This would
-//   involve switching the m_values array to use something more akin to ValueRecovery
-//   rather than the current ExitValue, albeit possibly with some hacks to better
-//   understand the kinds of places where the LLVM backend would put values.
+//   although again, it doesn't have to; it could just emit that code normally. We do
+//   all of these things through the patchpoint/stackmap LLVM intrinsics.
 //
 // - It could be extended to allow the backend to do its own exit hoisting, by using
 //   intrinsics (or meta-data, or something) to inform the backend that it's safe to
 //   make the predicate passed to 'exitIf()' more truthy.
-//
-// - It could be extended to support watchpoints (see webkit.org/b/113647) by making
-//   the predicate passed to 'exitIf()' be an intrinsic that the backend knows to be
-//   true at compile-time. The backend could then turn the conditional branch into a
-//   replaceable jump, much like the DFG does.
 
 struct OSRExit : public DFG::OSRExitBase {
     OSRExit(
diff --git a/Source/JavaScriptCore/tests/stress/many-sunken-locals.js b/Source/JavaScriptCore/tests/stress/many-sunken-locals.js
new file mode 100644 (file)
index 0000000..b50aee2
--- /dev/null
@@ -0,0 +1,27 @@
+function foo(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) {
+    if (a)
+        return 42;
+    else
+        return 63;
+}
+
+function bar(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) {
+    return foo(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
+}
+
+function baz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) {
+    return bar(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
+}
+
+function fuzz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) {
+    return baz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
+}
+
+function buzz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) {
+    return fuzz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
+}
+
+noInline(buzz);
+
+for (var i = 0; i < 10000; ++i)
+    buzz(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26);