Hoist and combine array bounds checks
https://bugs.webkit.org/show_bug.cgi?id=125433

Source/JavaScriptCore: 

Reviewed by Mark Hahnenberg.
        
This adds a phase for reasoning about overflow checks and array bounds checks. It's
block-local, and removes both overflow checks and bounds checks in one go.
        
This also improves reasoning about commutative operations, and CSE between
CheckOverflow and Unchecked arithmetic.
        
This strangely uncovered a DFG backend bug where we were trying to extract an int32
from a constant even when that constant was just simply a number. I fixed that bug.

* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGAbstractValue.cpp:
(JSC::DFG::AbstractValue::set):
* dfg/DFGArgumentsSimplificationPhase.cpp:
(JSC::DFG::ArgumentsSimplificationPhase::run):
* dfg/DFGArithMode.h:
(JSC::DFG::subsumes):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsic):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::pureCSE):
(JSC::DFG::CSEPhase::int32ToDoubleCSE):
(JSC::DFG::CSEPhase::performNodeCSE):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGEdge.cpp:
(JSC::DFG::Edge::dump):
* dfg/DFGEdge.h:
(JSC::DFG::Edge::sanitized):
(JSC::DFG::Edge::hash):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::valueOfInt32Constant):
* dfg/DFGInsertionSet.h:
(JSC::DFG::InsertionSet::insertConstant):
* dfg/DFGIntegerCheckCombiningPhase.cpp: Added.
(JSC::DFG::IntegerCheckCombiningPhase::IntegerCheckCombiningPhase):
(JSC::DFG::IntegerCheckCombiningPhase::run):
(JSC::DFG::IntegerCheckCombiningPhase::handleBlock):
(JSC::DFG::IntegerCheckCombiningPhase::rangeKeyAndAddend):
(JSC::DFG::IntegerCheckCombiningPhase::isValid):
(JSC::DFG::IntegerCheckCombiningPhase::insertAdd):
(JSC::DFG::IntegerCheckCombiningPhase::insertMustAdd):
(JSC::DFG::performIntegerCheckCombining):
* dfg/DFGIntegerCheckCombiningPhase.h: Added.
* dfg/DFGNode.h:
(JSC::DFG::Node::willHaveCodeGenOrOSR):
* dfg/DFGNodeType.h:
* dfg/DFGPlan.cpp:
(JSC::DFG::Plan::compileInThreadImpl):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileAdd):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::handleNode):
(JSC::DFG::StrengthReductionPhase::handleCommutativity):
* dfg/DFGTypeCheckHoistingPhase.cpp:
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
* jsc.cpp:
(GlobalObject::finishCreation):
(functionFalse):
* runtime/Identifier.h:
* runtime/Intrinsic.h:
* runtime/JSObject.h:
* tests/stress/get-by-id-untyped.js: Added.
(foo):
* tests/stress/inverted-additive-subsumption.js: Added.
(foo):
* tests/stress/redundant-add-overflow-checks.js: Added.
(foo):
* tests/stress/redundant-array-bounds-checks-addition-skip-first.js: Added.
(foo):
(arraycmp):
* tests/stress/redundant-array-bounds-checks-addition.js: Added.
(foo):
(arraycmp):
* tests/stress/redundant-array-bounds-checks-unchecked-addition.js: Added.
(foo):
(arraycmp):
* tests/stress/redundant-array-bounds-checks.js: Added.
(foo):
(arraycmp):
* tests/stress/tricky-array-bounds-checks.js: Added.
(foo):
(arraycmp):

Source/WTF: 

Reviewed by Mark Hahnenberg.

* GNUmakefile.list.am:
* WTF.vcxproj/WTF.vcxproj:
* WTF.xcodeproj/project.pbxproj:
* wtf/CMakeLists.txt:
* wtf/HashMethod.h: Added.
(WTF::HashMethod::operator()):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@164059 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index ea216e2..b3f346f 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -143,6 +143,7 @@
     dfg/DFGGraph.cpp
     dfg/DFGGraphSafepoint.cpp
     dfg/DFGInPlaceAbstractState.cpp
+    dfg/DFGIntegerCheckCombiningPhase.cpp
     dfg/DFGInvalidationPointInjectionPhase.cpp
     dfg/DFGJITCode.cpp
     dfg/DFGJITCompiler.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index a5d0b4e..5b9714e 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,113 @@
+2014-02-12  Filip Pizlo  <fpizlo@apple.com>
+
+        Hoist and combine array bounds checks
+        https://bugs.webkit.org/show_bug.cgi?id=125433
+
+        Reviewed by Mark Hahnenberg.
+        
+        This adds a phase for reasoning about overflow checks and array bounds checks. It's
+        block-local, and removes both overflow checks and bounds checks in one go.
+        
+        This also improves reasoning about commutative operations, and CSE between
+        CheckOverflow and Unchecked arithmetic.
+        
+        This strangely uncovered a DFG backend bug where we were trying to extract an int32
+        from a constant even when that constant was just simply a number. I fixed that bug.
+
+        * CMakeLists.txt:
+        * GNUmakefile.list.am:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGAbstractValue.cpp:
+        (JSC::DFG::AbstractValue::set):
+        * dfg/DFGArgumentsSimplificationPhase.cpp:
+        (JSC::DFG::ArgumentsSimplificationPhase::run):
+        * dfg/DFGArithMode.h:
+        (JSC::DFG::subsumes):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsic):
+        * dfg/DFGCSEPhase.cpp:
+        (JSC::DFG::CSEPhase::pureCSE):
+        (JSC::DFG::CSEPhase::int32ToDoubleCSE):
+        (JSC::DFG::CSEPhase::performNodeCSE):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGEdge.cpp:
+        (JSC::DFG::Edge::dump):
+        * dfg/DFGEdge.h:
+        (JSC::DFG::Edge::sanitized):
+        (JSC::DFG::Edge::hash):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGGraph.h:
+        (JSC::DFG::Graph::valueOfInt32Constant):
+        * dfg/DFGInsertionSet.h:
+        (JSC::DFG::InsertionSet::insertConstant):
+        * dfg/DFGIntegerCheckCombiningPhase.cpp: Added.
+        (JSC::DFG::IntegerCheckCombiningPhase::IntegerCheckCombiningPhase):
+        (JSC::DFG::IntegerCheckCombiningPhase::run):
+        (JSC::DFG::IntegerCheckCombiningPhase::handleBlock):
+        (JSC::DFG::IntegerCheckCombiningPhase::rangeKeyAndAddend):
+        (JSC::DFG::IntegerCheckCombiningPhase::isValid):
+        (JSC::DFG::IntegerCheckCombiningPhase::insertAdd):
+        (JSC::DFG::IntegerCheckCombiningPhase::insertMustAdd):
+        (JSC::DFG::performIntegerCheckCombining):
+        * dfg/DFGIntegerCheckCombiningPhase.h: Added.
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::willHaveCodeGenOrOSR):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPlan.cpp:
+        (JSC::DFG::Plan::compileInThreadImpl):
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileAdd):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStrengthReductionPhase.cpp:
+        (JSC::DFG::StrengthReductionPhase::handleNode):
+        (JSC::DFG::StrengthReductionPhase::handleCommutativity):
+        * dfg/DFGTypeCheckHoistingPhase.cpp:
+        (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
+        (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::LowerDFGToLLVM::compileNode):
+        * jsc.cpp:
+        (GlobalObject::finishCreation):
+        (functionFalse):
+        * runtime/Identifier.h:
+        * runtime/Intrinsic.h:
+        * runtime/JSObject.h:
+        * tests/stress/get-by-id-untyped.js: Added.
+        (foo):
+        * tests/stress/inverted-additive-subsumption.js: Added.
+        (foo):
+        * tests/stress/redundant-add-overflow-checks.js: Added.
+        (foo):
+        * tests/stress/redundant-array-bounds-checks-addition-skip-first.js: Added.
+        (foo):
+        (arraycmp):
+        * tests/stress/redundant-array-bounds-checks-addition.js: Added.
+        (foo):
+        (arraycmp):
+        * tests/stress/redundant-array-bounds-checks-unchecked-addition.js: Added.
+        (foo):
+        (arraycmp):
+        * tests/stress/redundant-array-bounds-checks.js: Added.
+        (foo):
+        (arraycmp):
+        * tests/stress/tricky-array-bounds-checks.js: Added.
+        (foo):
+        (arraycmp):
+
 2014-02-13  Filip Pizlo  <fpizlo@apple.com>
 
         FTL should be OK with __compact_unwind in a data section
diff --git a/Source/JavaScriptCore/GNUmakefile.list.am b/Source/JavaScriptCore/GNUmakefile.list.am
index fc40641..5682d82 100644
--- a/Source/JavaScriptCore/GNUmakefile.list.am
+++ b/Source/JavaScriptCore/GNUmakefile.list.am
@@ -317,9 +317,11 @@
 	Source/JavaScriptCore/dfg/DFGInPlaceAbstractState.h \
 	Source/JavaScriptCore/dfg/DFGInlineCacheWrapper.h \
 	Source/JavaScriptCore/dfg/DFGInlineCacheWrapperInlines.h \
+	Source/JavaScriptCore/dfg/DFGInsertionSet.h \
+	Source/JavaScriptCore/dfg/DFGIntegerCheckCombiningPhase.cpp \
+	Source/JavaScriptCore/dfg/DFGIntegerCheckCombiningPhase.h \
 	Source/JavaScriptCore/dfg/DFGInvalidationPointInjectionPhase.cpp \
 	Source/JavaScriptCore/dfg/DFGInvalidationPointInjectionPhase.h \
-	Source/JavaScriptCore/dfg/DFGInsertionSet.h \
 	Source/JavaScriptCore/dfg/DFGJITCode.cpp \
 	Source/JavaScriptCore/dfg/DFGJITCode.h \
 	Source/JavaScriptCore/dfg/DFGJITCompiler.cpp \
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
index 6ef6469..1005d63 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
@@ -395,6 +395,7 @@
     <ClCompile Include="..\dfg\DFGGraph.cpp" />
     <ClCompile Include="..\dfg\DFGGraphSafepoint.cpp" />
     <ClCompile Include="..\dfg\DFGInPlaceAbstractState.cpp" />
+    <ClCompile Include="..\dfg\DFGIntegerCheckCombiningPhase.cpp" />
     <ClCompile Include="..\dfg\DFGInvalidationPointInjectionPhase.cpp" />
     <ClCompile Include="..\dfg\DFGJITCode.cpp" />
     <ClCompile Include="..\dfg\DFGJITCompiler.cpp" />
@@ -939,6 +940,7 @@
     <ClInclude Include="..\dfg\DFGGraphSafepoint.h" />
     <ClInclude Include="..\dfg\DFGInPlaceAbstractState.h" />
     <ClInclude Include="..\dfg\DFGInsertionSet.h" />
+    <ClInclude Include="..\dfg\DFGIntegerCheckCombiningPhase.h" />
     <ClInclude Include="..\dfg\DFGInvalidationPointInjectionPhase.h" />
     <ClInclude Include="..\dfg\DFGJITCode.h" />
     <ClInclude Include="..\dfg\DFGJITCompiler.h" />
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index 08c5736..c0f8df0 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -210,6 +210,8 @@
 		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, ); }; };
+		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, ); }; };
@@ -1672,6 +1674,8 @@
 		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>"; };
+		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>"; };
@@ -4253,6 +4257,8 @@
 				A704D90017A0BAA8006BA554 /* DFGInPlaceAbstractState.cpp */,
 				A704D90117A0BAA8006BA554 /* DFGInPlaceAbstractState.h */,
 				0F2BDC1F151E803800CD8910 /* DFGInsertionSet.h */,
+				0F300B7918AB1B1400A6D72E /* DFGIntegerCheckCombiningPhase.cpp */,
+				0F300B7A18AB1B1400A6D72E /* DFGIntegerCheckCombiningPhase.h */,
 				0FC97F3718202119002C9B26 /* DFGInvalidationPointInjectionPhase.cpp */,
 				0FC97F3818202119002C9B26 /* DFGInvalidationPointInjectionPhase.h */,
 				0FEA0A2F170D40BF00BB722C /* DFGJITCode.cpp */,
@@ -5310,6 +5316,7 @@
 				0F4680CA14BBB16C00BFE272 /* LLIntCommon.h in Headers */,
 				0F4680D314BBD16700BFE272 /* LLIntData.h in Headers */,
 				0F38B01217CF078300B144D3 /* LLIntEntrypoint.h in Headers */,
+				0F300B7C18AB1B1400A6D72E /* DFGIntegerCheckCombiningPhase.h in Headers */,
 				0F4680A314BA7F8D00BFE272 /* LLIntExceptions.h in Headers */,
 				0F4680CB14BBB17200BFE272 /* LLIntOfflineAsmConfig.h in Headers */,
 				FED287B215EC9A5700DA8161 /* LLIntOpcode.h in Headers */,
@@ -6225,6 +6232,7 @@
 				A78853F917972629001440E4 /* IntendedStructureChain.cpp in Sources */,
 				147F39CF107EC37600427A48 /* InternalFunction.cpp in Sources */,
 				2AC922BB18A16182003CE0FB /* FTLDWARFDebugLineInfo.cpp in Sources */,
+				0F300B7B18AB1B1400A6D72E /* DFGIntegerCheckCombiningPhase.cpp in Sources */,
 				2ACCF3DE185FE26B0083E2AD /* DFGStoreBarrierElisionPhase.cpp in Sources */,
 				1429D7D40ED2128200B89619 /* Interpreter.cpp in Sources */,
 				1429D92F0ED22D7000B89619 /* JIT.cpp in Sources */,
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index 188f11d..7035890 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -1762,6 +1762,7 @@
     case ProfileWillCall:
     case ProfileDidCall:
     case Phantom:
+    case HardPhantom:
     case Check:
     case CountExecution:
     case CheckTierUpInLoop:
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp b/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
index 8bf9a01..6b73515 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
@@ -66,12 +66,12 @@
         m_futurePossibleStructure.clear();
         m_arrayModes = 0;
     }
-        
+    
     m_type = speculationFromValue(value);
     if (m_type == SpecInt52AsDouble)
         m_type = SpecInt52;
     m_value = value;
-        
+    
     checkConsistency();
 }
 
diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
index bcab71d..b99fd8c 100644
--- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
@@ -317,6 +317,7 @@
                 }
                     
                 case Phantom:
+                case HardPhantom:
                     // We don't care about phantom uses, since phantom uses are all about
                     // just keeping things alive for OSR exit. If something - like the
                     // CreateArguments - is just being kept alive, then this transformation
@@ -428,7 +429,8 @@
                     break;
                 }
                     
-                case Phantom: {
+                case Phantom:
+                case HardPhantom: {
                     // It's highly likely that we will have a Phantom referencing either
                     // CreateArguments, or a local op for the arguments register, or a
                     // local op for an arguments-aliased variable. In any of those cases,
diff --git a/Source/JavaScriptCore/dfg/DFGArithMode.h b/Source/JavaScriptCore/dfg/DFGArithMode.h
index 073ed6a..064f064 100644
--- a/Source/JavaScriptCore/dfg/DFGArithMode.h
+++ b/Source/JavaScriptCore/dfg/DFGArithMode.h
@@ -97,6 +97,31 @@
     return true;
 }
 
+inline bool subsumes(Arith::Mode earlier, Arith::Mode later)
+{
+    switch (earlier) {
+    case Arith::CheckOverflow:
+        switch (later) {
+        case Arith::Unchecked:
+        case Arith::CheckOverflow:
+            return true;
+        default:
+            return false;
+        }
+    case Arith::CheckOverflowAndNegativeZero:
+        switch (later) {
+        case Arith::Unchecked:
+        case Arith::CheckOverflow:
+        case Arith::CheckOverflowAndNegativeZero:
+            return true;
+        default:
+            return false;
+        }
+    default:
+        return earlier == later;
+    }
+}
+
 } } // namespace JSC::DFG
 
 namespace WTF {
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index d6bc292..0254bb2 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -1668,6 +1668,11 @@
         return true;
     }
         
+    case DFGTrue: {
+        set(VirtualRegister(resultOperand), getJSConstantForValue(jsBoolean(true), 0));
+        return true;
+    }
+        
     default:
         return false;
     }
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index 8c04fba..d6db78b 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -127,9 +127,9 @@
 
     Node* pureCSE(Node* node)
     {
-        Edge child1 = node->child1();
-        Edge child2 = node->child2();
-        Edge child3 = node->child3();
+        Edge child1 = node->child1().sanitized();
+        Edge child2 = node->child2().sanitized();
+        Edge child3 = node->child3().sanitized();
         
         for (unsigned i = endIndexForPureCSE(); i--;) {
             Node* otherNode = m_currentBlock->at(i);
@@ -139,24 +139,19 @@
             if (node->op() != otherNode->op())
                 continue;
             
-            if (node->hasArithMode()) {
-                if (node->arithMode() != otherNode->arithMode())
-                    continue;
-            }
-            
-            Edge otherChild = otherNode->child1();
+            Edge otherChild = otherNode->child1().sanitized();
             if (!otherChild)
                 return otherNode;
             if (otherChild != child1)
                 continue;
             
-            otherChild = otherNode->child2();
+            otherChild = otherNode->child2().sanitized();
             if (!otherChild)
                 return otherNode;
             if (otherChild != child2)
                 continue;
             
-            otherChild = otherNode->child3();
+            otherChild = otherNode->child3().sanitized();
             if (!otherChild)
                 return otherNode;
             if (otherChild != child3)
@@ -175,7 +170,7 @@
                 return 0;
             switch (otherNode->op()) {
             case Int32ToDouble:
-                if (otherNode->child1() == node->child1())
+                if (otherNode->child1().sanitized() == node->child1().sanitized())
                     return otherNode;
                 break;
             default:
@@ -1095,12 +1090,6 @@
         case BitRShift:
         case BitLShift:
         case BitURShift:
-        case ArithAdd:
-        case ArithSub:
-        case ArithNegate:
-        case ArithMul:
-        case ArithMod:
-        case ArithDiv:
         case ArithAbs:
         case ArithMin:
         case ArithMax:
@@ -1115,7 +1104,6 @@
         case IsString:
         case IsObject:
         case IsFunction:
-        case DoubleAsInt32:
         case LogicalNot:
         case SkipTopScope:
         case SkipScope:
@@ -1132,6 +1120,28 @@
             setReplacement(pureCSE(node));
             break;
             
+        case ArithAdd:
+        case ArithSub:
+        case ArithNegate:
+        case ArithMul:
+        case ArithDiv:
+        case ArithMod:
+        case UInt32ToNumber:
+        case DoubleAsInt32: {
+            if (cseMode == StoreElimination)
+                break;
+            Node* candidate = pureCSE(node);
+            if (!candidate)
+                break;
+            if (!subsumes(candidate->arithMode(), node->arithMode())) {
+                if (!subsumes(node->arithMode(), candidate->arithMode()))
+                    break;
+                candidate->setArithMode(node->arithMode());
+            }
+            setReplacement(candidate);
+            break;
+        }
+            
         case Int32ToDouble:
             if (cseMode == StoreElimination)
                 break;
@@ -1407,6 +1417,9 @@
             
         case Phantom:
             // FIXME: we ought to remove Phantom's that have no children.
+            // NB. It would be incorrect to do this for HardPhantom. In fact, the whole point
+            // of HardPhantom is that we *don't* do this for HardPhantoms, since they signify
+            // a more strict kind of liveness than the Phantom bytecode liveness.
             eliminateIrrelevantPhantomChildren(node);
             break;
             
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index 2e4da2e..dc7d1eb 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -88,6 +88,7 @@
     case WeakJSConstant:
     case Identity:
     case Phantom:
+    case HardPhantom:
     case Breakpoint:
     case ProfileWillCall:
     case ProfileDidCall:
diff --git a/Source/JavaScriptCore/dfg/DFGEdge.cpp b/Source/JavaScriptCore/dfg/DFGEdge.cpp
index ad43a22..adf1f73 100644
--- a/Source/JavaScriptCore/dfg/DFGEdge.cpp
+++ b/Source/JavaScriptCore/dfg/DFGEdge.cpp
@@ -36,12 +36,12 @@
 
 void Edge::dump(PrintStream& out) const
 {
-    if (useKind() != UntypedUse) {
+    if (useKindUnchecked() != UntypedUse) {
         if (needsCheck())
             out.print("Check:");
         out.print(useKind(), ":");
     }
-    if (doesKill())
+    if (DFG::doesKill(killStatusUnchecked()))
         out.print("Kill:");
     out.print(node());
 }
diff --git a/Source/JavaScriptCore/dfg/DFGEdge.h b/Source/JavaScriptCore/dfg/DFGEdge.h
index e641b65..0aadf25 100644
--- a/Source/JavaScriptCore/dfg/DFGEdge.h
+++ b/Source/JavaScriptCore/dfg/DFGEdge.h
@@ -153,6 +153,17 @@
     bool doesNotKill() const { return !doesKill(); }
     
     bool isSet() const { return !!node(); }
+
+    Edge sanitized() const
+    {
+        Edge result = *this;
+#if USE(JSVALUE64)
+        result.m_encodedWord = makeWord(node(), useKindUnchecked(), NeedsCheck, DoesNotKill);
+#else
+        result.m_encodedWord = makeWord(useKindUnchecked(), NeedsCheck, DoesNotKill);
+#endif
+        return result;
+    }
     
     typedef void* Edge::*UnspecifiedBoolType;
     operator UnspecifiedBoolType*() const { return reinterpret_cast<UnspecifiedBoolType*>(isSet()); }
@@ -173,6 +184,15 @@
     }
     
     void dump(PrintStream&) const;
+    
+    unsigned hash() const
+    {
+#if USE(JSVALUE64)
+        return IntHash<uintptr_t>::hash(m_encodedWord);
+#else
+        return PtrHash<Node*>::hash(m_node) + m_encodedWord;
+#endif
+    }
 
 private:
     friend class AdjacencyList;
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index dab217b..33c7cef 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -941,6 +941,7 @@
         case DoubleAsInt32:
         case Int32ToDouble:
         case ValueToInt32:
+        case HardPhantom: // HardPhantom would be trivial to handle but anyway we assert that we won't see it here yet.
             // 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.
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h
index 1b2f230..1631ecc 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.h
+++ b/Source/JavaScriptCore/dfg/DFGGraph.h
@@ -361,7 +361,13 @@
     }
     int32_t valueOfInt32Constant(Node* node)
     {
-        return valueOfJSConstant(node).asInt32();
+        JSValue value = valueOfJSConstant(node);
+        if (!value.isInt32()) {
+            dataLog("Value isn't int32: ", value, "\n");
+            dump();
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+        return value.asInt32();
     }
     double valueOfNumberConstant(Node* node)
     {
diff --git a/Source/JavaScriptCore/dfg/DFGInsertionSet.h b/Source/JavaScriptCore/dfg/DFGInsertionSet.h
index 8d76c45..1ffd3d9 100644
--- a/Source/JavaScriptCore/dfg/DFGInsertionSet.h
+++ b/Source/JavaScriptCore/dfg/DFGInsertionSet.h
@@ -56,7 +56,7 @@
     {
         return insert(Insertion(index, element));
     }
-
+    
 #define DFG_DEFINE_INSERT_NODE(templatePre, templatePost, typeParams, valueParamsComma, valueParams, valueArgs) \
     templatePre typeParams templatePost Node* insertNode(size_t index, SpeculatedType type valueParamsComma valueParams) \
     { \
@@ -65,6 +65,20 @@
     DFG_VARIADIC_TEMPLATE_FUNCTION(DFG_DEFINE_INSERT_NODE)
 #undef DFG_DEFINE_INSERT_NODE
     
+    Node* insertConstant(size_t index, NodeOrigin origin, JSValue value)
+    {
+        unsigned constantReg =
+            m_graph.constantRegisterForConstant(value);
+        return insertNode(
+            index, speculationFromValue(value), JSConstant, origin,
+            OpInfo(constantReg));
+    }
+    
+    Node* insertConstant(size_t index, CodeOrigin origin, JSValue value)
+    {
+        return insertConstant(index, NodeOrigin(origin), value);
+    }
+
     void execute(BasicBlock* block)
     {
         executeInsertions(*block, m_insertions);
diff --git a/Source/JavaScriptCore/dfg/DFGIntegerCheckCombiningPhase.cpp b/Source/JavaScriptCore/dfg/DFGIntegerCheckCombiningPhase.cpp
new file mode 100644
index 0000000..7b6be73e
--- /dev/null
+++ b/Source/JavaScriptCore/dfg/DFGIntegerCheckCombiningPhase.cpp
@@ -0,0 +1,405 @@
+/*
+ * 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"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGIntegerCheckCombiningPhase.h"
+
+#include "DFGGraph.h"
+#include "DFGInsertionSet.h"
+#include "DFGPhase.h"
+#include "DFGPredictionPropagationPhase.h"
+#include "DFGVariableAccessDataDump.h"
+#include "JSCInlines.h"
+#include <unordered_map>
+#include <wtf/HashMethod.h>
+
+namespace JSC { namespace DFG {
+
+namespace {
+
+static const bool verbose = false;
+
+enum RangeKind {
+    InvalidRangeKind,
+    
+    // This means we did ArithAdd with CheckOverflow.
+    Addition,
+    
+    // This means we did CheckInBounds on some length.
+    ArrayBounds
+};
+
+struct RangeKey {
+    RangeKey()
+        : m_kind(InvalidRangeKind)
+        , m_key(nullptr)
+    {
+    }
+    
+    static RangeKey addition(Edge edge)
+    {
+        RangeKey result;
+        result.m_kind = Addition;
+        result.m_source = edge.sanitized();
+        result.m_key = 0;
+        return result;
+    }
+    
+    static RangeKey arrayBounds(Edge edge, Node* key)
+    {
+        RangeKey result;
+        result.m_kind = ArrayBounds;
+        result.m_source = edge.sanitized();
+        result.m_key = key;
+        return result;
+    }
+    
+    bool operator!() const { return m_kind == InvalidRangeKind; }
+    
+    unsigned hash() const
+    {
+        return m_kind + m_source.hash() + PtrHash<Node*>::hash(m_key);
+    }
+    
+    bool operator==(const RangeKey& other) const
+    {
+        return m_kind == other.m_kind
+            && m_source == other.m_source
+            && m_key == other.m_key;
+    }
+    
+    void dump(PrintStream& out) const
+    {
+        switch (m_kind) {
+        case InvalidRangeKind:
+            out.print("InvalidRangeKind(");
+            break;
+        case Addition:
+            out.print("Addition(");
+            break;
+        case ArrayBounds:
+            out.print("ArrayBounds(");
+            break;
+        }
+        out.print(m_source, ", ", m_key, ")");
+    }
+    
+    RangeKind m_kind;
+    Edge m_source;
+    Node* m_key;
+};
+
+struct RangeKeyAndAddend {
+    RangeKeyAndAddend()
+        : m_addend(0)
+    {
+    }
+    
+    RangeKeyAndAddend(RangeKey key, int32_t addend)
+        : m_key(key)
+        , m_addend(addend)
+    {
+    }
+    
+    bool operator!() const { return !m_key && !m_addend; }
+    
+    void dump(PrintStream& out) const
+    {
+        out.print(m_key, " + ", m_addend);
+    }
+    
+    RangeKey m_key;
+    int32_t m_addend;
+};
+
+struct Range {
+    Range()
+        : m_minBound(0)
+        , m_maxBound(0)
+        , m_count(0)
+        , m_hoisted(false)
+    {
+    }
+    
+    void dump(PrintStream& out) const
+    {
+        out.print("(", m_minBound, " @", m_minOrigin, ") .. (", m_maxBound, " @", m_maxOrigin, "), count = ", m_count, ", hoisted = ", m_hoisted);
+    }
+    
+    int32_t m_minBound;
+    CodeOrigin m_minOrigin;
+    int32_t m_maxBound;
+    CodeOrigin m_maxOrigin;
+    unsigned m_count; // If this is zero then the bounds won't necessarily make sense.
+    bool m_hoisted;
+};
+
+} // anonymous namespace
+
+class IntegerCheckCombiningPhase : public Phase {
+public:
+    IntegerCheckCombiningPhase(Graph& graph)
+        : Phase(graph, "integer check combining")
+        , m_insertionSet(graph)
+    {
+    }
+    
+    bool run()
+    {
+        ASSERT(m_graph.m_form == SSA);
+        
+        m_changed = false;
+        
+        for (BlockIndex blockIndex = blockIndex = m_graph.numBlocks(); blockIndex--;)
+            handleBlock(blockIndex);
+        
+        return m_changed;
+    }
+
+private:
+    void handleBlock(BlockIndex blockIndex)
+    {
+        BasicBlock* block = m_graph.block(blockIndex);
+        if (!block)
+            return;
+        
+        m_map.clear();
+        
+        // First we collect Ranges. If operations within the range have enough redundancy,
+        // we hoist. And then we remove additions and checks that fall within the max range.
+        
+        for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
+            Node* node = block->at(nodeIndex);
+            RangeKeyAndAddend data = rangeKeyAndAddend(node);
+            if (verbose)
+                dataLog("For ", node, ": ", data, "\n");
+            if (!data)
+                continue;
+            
+            Range& range = m_map[data.m_key];
+            if (verbose)
+                dataLog("    Range: ", range, "\n");
+            if (range.m_count) {
+                if (data.m_addend > range.m_maxBound) {
+                    range.m_maxBound = data.m_addend;
+                    range.m_maxOrigin = node->origin.semantic;
+                } else if (data.m_addend < range.m_minBound) {
+                    range.m_minBound = data.m_addend;
+                    range.m_minOrigin = node->origin.semantic;
+                }
+            } else {
+                range.m_maxBound = data.m_addend;
+                range.m_minBound = data.m_addend;
+                range.m_minOrigin = node->origin.semantic;
+                range.m_maxOrigin = node->origin.semantic;
+            }
+            range.m_count++;
+            if (verbose)
+                dataLog("    New range: ", range, "\n");
+        }
+        
+        for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
+            Node* node = block->at(nodeIndex);
+            RangeKeyAndAddend data = rangeKeyAndAddend(node);
+            if (!data)
+                continue;
+            Range range = m_map[data.m_key];
+            if (!isValid(data.m_key, range))
+                continue;
+            
+            // Do the hoisting.
+            if (!range.m_hoisted) {
+                switch (data.m_key.m_kind) {
+                case Addition: {
+                    if (range.m_minBound < 0) {
+                        insertMustAdd(
+                            nodeIndex, NodeOrigin(range.m_minOrigin, node->origin.forExit),
+                            data.m_key.m_source, range.m_minBound);
+                    }
+                    if (range.m_maxBound > 0) {
+                        insertMustAdd(
+                            nodeIndex, NodeOrigin(range.m_maxOrigin, node->origin.forExit),
+                            data.m_key.m_source, range.m_maxBound);
+                    }
+                    break;
+                }
+                
+                case ArrayBounds: {
+                    Node* minNode;
+                    Node* maxNode;
+                    
+                    if (!data.m_key.m_source) {
+                        minNode = 0;
+                        maxNode = m_insertionSet.insertConstant(
+                            nodeIndex, range.m_maxOrigin, jsNumber(range.m_maxBound));
+                    } else {
+                        minNode = insertAdd(
+                            nodeIndex, NodeOrigin(range.m_minOrigin, node->origin.forExit),
+                            data.m_key.m_source, range.m_minBound, Arith::Unchecked);
+                        maxNode = insertAdd(
+                            nodeIndex, NodeOrigin(range.m_maxOrigin, node->origin.forExit),
+                            data.m_key.m_source, range.m_maxBound, Arith::Unchecked);
+                    }
+                    
+                    if (minNode) {
+                        m_insertionSet.insertNode(
+                            nodeIndex, SpecNone, CheckInBounds, node->origin,
+                            Edge(minNode, Int32Use), Edge(data.m_key.m_key, Int32Use));
+                    }
+                    m_insertionSet.insertNode(
+                        nodeIndex, SpecNone, CheckInBounds, node->origin,
+                        Edge(maxNode, Int32Use), Edge(data.m_key.m_key, Int32Use));
+                    break;
+                }
+                
+                default:
+                    RELEASE_ASSERT_NOT_REACHED();
+                }
+                
+                m_changed = true;
+                m_map[data.m_key].m_hoisted = true;
+            }
+            
+            // Do the elimination.
+            switch (data.m_key.m_kind) {
+            case Addition:
+                node->setArithMode(Arith::Unchecked);
+                m_changed = true;
+                break;
+                
+            case ArrayBounds:
+                node->convertToPhantom();
+                m_changed = true;
+                break;
+                
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+            }
+        }
+        
+        m_insertionSet.execute(block);
+    }
+    
+    RangeKeyAndAddend rangeKeyAndAddend(Node* node)
+    {
+        switch (node->op()) {
+        case ArithAdd: {
+            if (node->arithMode() != Arith::CheckOverflow
+                && node->arithMode() != Arith::CheckOverflowAndNegativeZero)
+                break;
+            if (!m_graph.isInt32Constant(node->child2().node()))
+                break;
+            return RangeKeyAndAddend(
+                RangeKey::addition(node->child1()),
+                m_graph.valueOfInt32Constant(node->child2().node()));
+        }
+                
+        case CheckInBounds: {
+            Edge source;
+            int32_t addend;
+            Node* key = node->child2().node();
+            
+            Edge index = node->child1();
+            
+            if (m_graph.isInt32Constant(index.node())) {
+                source = Edge();
+                addend = m_graph.valueOfInt32Constant(index.node());
+            } else if (
+                index->op() == ArithAdd
+                && index->isBinaryUseKind(Int32Use)
+                && m_graph.isInt32Constant(index->child2().node())) {
+                source = index->child1();
+                addend = m_graph.valueOfInt32Constant(index->child2().node());
+            } else {
+                source = index;
+                addend = 0;
+            }
+            
+            return RangeKeyAndAddend(RangeKey::arrayBounds(source, key), addend);
+        }
+                
+        default:
+            break;
+        }
+        
+        return RangeKeyAndAddend();
+    }
+    
+    bool isValid(const RangeKey& key, const Range& range)
+    {
+        if (range.m_count < 2)
+            return false;
+        
+        switch (key.m_kind) {
+        case ArrayBounds:
+            return (range.m_maxBound - range.m_minBound) >= 0;
+            
+        default:
+            return true;
+        }
+    }
+    
+    Node* insertAdd(
+        unsigned nodeIndex, NodeOrigin origin, Edge source, int32_t addend,
+        Arith::Mode arithMode = Arith::CheckOverflow)
+    {
+        if (!addend)
+            return source.node();
+        return m_insertionSet.insertNode(
+            nodeIndex, source->prediction(), ArithAdd, origin, OpInfo(arithMode),
+            source, Edge(
+                m_insertionSet.insertConstant(nodeIndex, origin, jsNumber(addend)),
+                source.useKind()));
+    }
+    
+    Node* insertMustAdd(
+        unsigned nodeIndex, NodeOrigin origin, Edge source, int32_t addend)
+    {
+        Node* result = insertAdd(nodeIndex, origin, source, addend);
+        m_insertionSet.insertNode(
+            nodeIndex, SpecNone, HardPhantom, origin, Edge(result, UntypedUse));
+        return result;
+    }
+    
+    typedef std::unordered_map<RangeKey, Range, HashMethod<RangeKey>> RangeMap;
+    RangeMap m_map;
+    
+    InsertionSet m_insertionSet;
+    bool m_changed;
+};
+    
+bool performIntegerCheckCombining(Graph& graph)
+{
+    SamplingRegion samplingRegion("DFG Integer Check Combining Phase");
+    return runPhase<IntegerCheckCombiningPhase>(graph);
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
diff --git a/Source/JavaScriptCore/dfg/DFGIntegerCheckCombiningPhase.h b/Source/JavaScriptCore/dfg/DFGIntegerCheckCombiningPhase.h
new file mode 100644
index 0000000..6abec03
--- /dev/null
+++ b/Source/JavaScriptCore/dfg/DFGIntegerCheckCombiningPhase.h
@@ -0,0 +1,44 @@
+/*
+ * 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 DFGIntegerCheckCombiningPhase_h
+#define DFGIntegerCheckCombiningPhase_h
+
+#if ENABLE(DFG_JIT)
+
+namespace JSC { namespace DFG {
+
+class Graph;
+
+// Removes overflow checks and out-of-bounds checks by hoisting them.
+
+bool performIntegerCheckCombining(Graph&);
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGIntegerCheckCombiningPhase_h
+
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index 376bbbf..beddaed 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -1198,6 +1198,7 @@
         case PhantomArguments:
             return true;
         case Phantom:
+        case HardPhantom:
             return child1().useKindUnchecked() != UntypedUse || child2().useKindUnchecked() != UntypedUse || child3().useKindUnchecked() != UntypedUse;
         default:
             return shouldGenerate();
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 3d2f80b..dfa6c34 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -62,6 +62,7 @@
     macro(ZombieHint, NodeDoesNotExit) \
     macro(GetArgument, NodeResultJS | NodeMustGenerate) \
     macro(Phantom, NodeMustGenerate) \
+    macro(HardPhantom, NodeMustGenerate) /* Like Phantom, but we never remove any of its children. */ \
     macro(Check, 0) /* Used if we want just a type check but not liveness. DCE eithers kills this or converts it to Phantom. */\
     macro(Upsilon, NodeDoesNotExit | NodeRelevantToOSR) \
     macro(Phi, NodeDoesNotExit | NodeRelevantToOSR) \
diff --git a/Source/JavaScriptCore/dfg/DFGPlan.cpp b/Source/JavaScriptCore/dfg/DFGPlan.cpp
index 212b45f..58c6753 100644
--- a/Source/JavaScriptCore/dfg/DFGPlan.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPlan.cpp
@@ -43,6 +43,7 @@
 #include "DFGFlushLivenessAnalysisPhase.h"
 #include "DFGFixupPhase.h"
 #include "DFGGraphSafepoint.h"
+#include "DFGIntegerCheckCombiningPhase.h"
 #include "DFGInvalidationPointInjectionPhase.h"
 #include "DFGJITCompiler.h"
 #include "DFGLICMPhase.h"
@@ -300,9 +301,11 @@
         performCPSRethreading(dfg);
         performSSAConversion(dfg);
         performSSALowering(dfg);
+        performCSE(dfg);
         performLivenessAnalysis(dfg);
         performCFA(dfg);
         performLICM(dfg);
+        performIntegerCheckCombining(dfg);
         performCSE(dfg);
         performLivenessAnalysis(dfg);
         performCFA(dfg);
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index d9863c6..19d9428 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -507,7 +507,8 @@
         case Int52ToValue:
         case Int52ToDouble:
         case CheckInBounds:
-        case ValueToInt32: {
+        case ValueToInt32:
+        case HardPhantom: {
             // This node should never be visible at this stage of compilation. It is
             // inserted by fixup(), which follows this phase.
             RELEASE_ASSERT_NOT_REACHED();
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index b6cd5dc..ae3486a 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -122,6 +122,7 @@
     case ZombieHint:
     case GetArgument:
     case Phantom:
+    case HardPhantom:
     case Upsilon:
     case Phi:
     case Flush:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 641335b..08744a2 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -2630,7 +2630,7 @@
     case Int32Use: {
         ASSERT(!shouldCheckNegativeZero(node->arithMode()));
         
-        if (isNumberConstant(node->child1().node())) {
+        if (isInt32Constant(node->child1().node())) {
             int32_t imm1 = valueOfInt32Constant(node->child1().node());
             SpeculateInt32Operand op2(this, node->child2());
             GPRTemporary result(this);
@@ -2645,7 +2645,7 @@
             return;
         }
         
-        if (isNumberConstant(node->child2().node())) {
+        if (isInt32Constant(node->child2().node())) {
             SpeculateInt32Operand op1(this, node->child1());
             int32_t imm2 = valueOfInt32Constant(node->child2().node());
             GPRTemporary result(this);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 8e6fd4a..fde2b14 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -4659,6 +4659,7 @@
         break;
 
     case Phantom:
+    case HardPhantom:
         DFG_NODE_DO_TO_CHILDREN(m_jit.graph(), node, speculate);
         noResult(node);
         break;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 87e5d2d..6ea2476 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -4915,6 +4915,7 @@
         break;
 
     case Phantom:
+    case HardPhantom:
         DFG_NODE_DO_TO_CHILDREN(m_jit.graph(), node, speculate);
         noResult(node);
         break;
diff --git a/Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp b/Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp
index 27fa678..41472cf 100644
--- a/Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp
@@ -71,13 +71,8 @@
     {
         switch (m_node->op()) {
         case BitOr:
-            if (m_node->child1()->isConstant()) {
-                JSValue op1 = m_graph.valueOfJSConstant(m_node->child1().node());
-                if (op1.isInt32() && !op1.asInt32()) {
-                    convertToIdentityOverChild2();
-                    break;
-                }
-            }
+            handleCommutativity();
+
             if (m_node->child2()->isConstant()) {
                 JSValue op2 = m_graph.valueOfJSConstant(m_node->child2().node());
                 if (op2.isInt32() && !op2.asInt32()) {
@@ -87,6 +82,11 @@
             }
             break;
             
+        case BitXor:
+        case BitAnd:
+            handleCommutativity();
+            break;
+            
         case BitLShift:
         case BitRShift:
         case BitURShift:
@@ -112,6 +112,38 @@
             }
             break;
             
+        case ArithAdd:
+            handleCommutativity();
+            
+            if (m_graph.isInt32Constant(m_node->child2().node())) {
+                int32_t value = m_graph.valueOfInt32Constant(
+                    m_node->child2().node());
+                if (!value) {
+                    convertToIdentityOverChild1();
+                    break;
+                }
+            }
+            break;
+            
+        case ArithMul:
+            handleCommutativity();
+            break;
+            
+        case ArithSub:
+            if (m_graph.isInt32Constant(m_node->child2().node())
+                && m_node->isBinaryUseKind(Int32Use)) {
+                int32_t value = m_graph.valueOfInt32Constant(m_node->child2().node());
+                if (-value != value) {
+                    m_node->setOp(ArithAdd);
+                    m_node->child2().setNode(
+                        m_insertionSet.insertConstant(
+                            m_nodeIndex, m_node->origin, jsNumber(-value)));
+                    m_changed = true;
+                    break;
+                }
+            }
+            break;
+            
         case GetArrayLength:
             if (JSArrayBufferView* view = m_graph.tryGetFoldableViewForChild1(m_node))
                 foldTypedArrayPropertyToConstant(view, jsNumber(view->length()));
@@ -179,6 +211,28 @@
             m_nodeIndex, SpecNone, Phantom, m_node->origin, m_node->children);
     }
     
+    void handleCommutativity()
+    {
+        // If the right side is a constant then there is nothing left to do.
+        if (m_node->child2()->hasConstant())
+            return;
+        
+        // This case ensures that optimizations that look for x + const don't also have
+        // to look for const + x.
+        if (m_node->child1()->hasConstant()) {
+            std::swap(m_node->child1(), m_node->child2());
+            m_changed = true;
+            return;
+        }
+        
+        // This case ensures that CSE is commutativity-aware.
+        if (m_node->child1().node() > m_node->child2().node()) {
+            std::swap(m_node->child1(), m_node->child2());
+            m_changed = true;
+            return;
+        }
+    }
+    
     InsertionSet m_insertionSet;
     BasicBlock* m_block;
     unsigned m_nodeIndex;
diff --git a/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
index f8a2013..9d21dd1 100644
--- a/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
@@ -244,6 +244,7 @@
                 case GetIndexedPropertyStorage:
                 case GetTypedArrayByteOffset:
                 case Phantom:
+                case HardPhantom:
                 case MovHint:
                     // Don't count these uses.
                     break;
@@ -342,6 +343,7 @@
                 case GetArrayLength:
                 case GetIndexedPropertyStorage:
                 case Phantom:
+                case HardPhantom:
                 case MovHint:
                     // Don't count these uses.
                     break;
diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
index 18c154e..c8b3649 100644
--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
@@ -50,6 +50,7 @@
     case MovHint:
     case ZombieHint:
     case Phantom:
+    case HardPhantom:
     case Flush:
     case PhantomLocal:
     case SetArgument:
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
index b3acf27..d78e65c 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
@@ -301,6 +301,7 @@
             compileZombieHint();
             break;
         case Phantom:
+        case HardPhantom:
             compilePhantom();
             break;
         case ToThis:
diff --git a/Source/JavaScriptCore/jsc.cpp b/Source/JavaScriptCore/jsc.cpp
index 03bcdf8..a36cd4e 100644
--- a/Source/JavaScriptCore/jsc.cpp
+++ b/Source/JavaScriptCore/jsc.cpp
@@ -237,6 +237,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*);
 
 #if ENABLE(SAMPLING_FLAGS)
 static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
@@ -370,6 +371,8 @@
         addFunction(vm, "getElement", functionGetElement, 1);
         addFunction(vm, "setElementRoot", functionSetElementRoot, 2);
         
+        putDirectNativeFunction(vm, this, Identifier(&vm, "DFGTrue"), 0, functionFalse, DFGTrue, DontEnum | JSC::Function);
+        
         JSArray* array = constructEmptyArray(globalExec(), 0);
         for (size_t i = 0; i < arguments.size(); ++i)
             array->putDirectIndex(globalExec(), i, jsString(globalExec(), arguments[i]));
@@ -719,6 +722,11 @@
 #endif
 }
 
+EncodedJSValue JSC_HOST_CALL functionFalse(ExecState*)
+{
+    return JSValue::encode(jsBoolean(false));
+}
+
 // Use SEH for Release builds only to get rid of the crash report dialog
 // (luckily the same tests fail in Release and Debug builds so far). Need to
 // be in a separate main function because the jscmain function requires object
diff --git a/Source/JavaScriptCore/runtime/Identifier.h b/Source/JavaScriptCore/runtime/Identifier.h
index d8037eb..003ede3 100644
--- a/Source/JavaScriptCore/runtime/Identifier.h
+++ b/Source/JavaScriptCore/runtime/Identifier.h
@@ -95,7 +95,7 @@
         static bool equal(const StringImpl* a, const StringImpl* b) { return ::equal(a, b); }
 
         // Only to be used with string literals.
-        static PassRef<StringImpl> add(VM*, const char*);
+        JS_EXPORT_PRIVATE static PassRef<StringImpl> add(VM*, const char*);
         JS_EXPORT_PRIVATE static PassRef<StringImpl> add(ExecState*, const char*);
 
     private:
diff --git a/Source/JavaScriptCore/runtime/Intrinsic.h b/Source/JavaScriptCore/runtime/Intrinsic.h
index 1f741da..f7930f3 100644
--- a/Source/JavaScriptCore/runtime/Intrinsic.h
+++ b/Source/JavaScriptCore/runtime/Intrinsic.h
@@ -53,7 +53,10 @@
     IMulIntrinsic,
     ArrayIteratorNextValueIntrinsic,
     ArrayIteratorNextKeyIntrinsic,
-    ArrayIteratorNextGenericIntrinsic
+    ArrayIteratorNextGenericIntrinsic,
+    
+    // Debugging intrinsics
+    DFGTrue
 };
 
 } // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h
index c872f62..a9bd539 100644
--- a/Source/JavaScriptCore/runtime/JSObject.h
+++ b/Source/JavaScriptCore/runtime/JSObject.h
@@ -580,7 +580,7 @@
     void putDirect(VM& vm, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(vm, this, value); }
     void putDirectUndefined(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); }
 
-    void putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes);
+    JS_EXPORT_PRIVATE void putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes);
     void putDirectBuiltinFunction(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes);
     void putDirectNativeFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes);
 
diff --git a/Source/JavaScriptCore/tests/stress/get-by-id-untyped.js b/Source/JavaScriptCore/tests/stress/get-by-id-untyped.js
new file mode 100644
index 0000000..8995d80
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/get-by-id-untyped.js
@@ -0,0 +1,21 @@
+function foo(o) {
+    return o.f;
+}
+
+noInline(foo);
+
+String.prototype.f = 42;
+Number.prototype.f = 24;
+
+for (var i = 0; i < 100000; ++i) {
+    var result = foo("hello");
+    if (result != 42)
+        throw "Error: bad result for string: " + result;
+    result = foo(13);
+    if (result != 24)
+        throw "Error: bad result for number: " + result;
+    result = foo({f:84});
+    if (result != 84)
+        throw "Error: bad result for object: " + result;
+}
+
diff --git a/Source/JavaScriptCore/tests/stress/inverted-additive-subsumption.js b/Source/JavaScriptCore/tests/stress/inverted-additive-subsumption.js
new file mode 100644
index 0000000..d41fd1f
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/inverted-additive-subsumption.js
@@ -0,0 +1,15 @@
+function foo(x) {
+    return ((x + 1) | 0) + (x + 1);
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100000; ++i) {
+    var result = foo(i);
+    if (result != (i + 1) * 2)
+        throw "Error: bad result for i = " + i + ": " + result;
+}
+
+var result = foo(2147483647);
+if (result != ((2147483647 + 1) | 0) + (2147483647 + 1))
+    throw "Error: bad result for 2147483647: " + result;
diff --git a/Source/JavaScriptCore/tests/stress/redundant-add-overflow-checks.js b/Source/JavaScriptCore/tests/stress/redundant-add-overflow-checks.js
new file mode 100644
index 0000000..b435ff3
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/redundant-add-overflow-checks.js
@@ -0,0 +1,18 @@
+function foo(x) {
+    return (x + 0) + (x + 1) + (x + 2) + (x + 3) + (x + 4) + (x + 5) + (x + 6) + (x + 7) + (x + 8) + (x + 9) + (x + 10);
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100000; ++i) {
+    var result = foo(i);
+    if (result != i * 11 + 55)
+        throw "Error: bad result for i = " + i + ": " + result;
+}
+
+for (var i = 2147483628; i <= 2147483647; i++) {
+    var result = foo(i);
+    if (result != i * 11 + 55)
+        throw "Error: bad result for i = " + i + ": " + result;
+}
+
diff --git a/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks-addition-skip-first.js b/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks-addition-skip-first.js
new file mode 100644
index 0000000..e8a0391
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks-addition-skip-first.js
@@ -0,0 +1,50 @@
+function foo(a, i) {
+    return [a[i], a[i + 1], a[i + 2], a[i + 3], a[i + 4], a[i + 5], a[i + 6], a[i + 7]];
+}
+
+noInline(foo);
+
+function arraycmp(a, b) {
+    if (a.length != b.length)
+        return false;
+    for (var i = 0; i < a.length; ++i) {
+        if (a[i] != b[i])
+            return false;
+    }
+    return true;
+}
+
+for (var i = 0; i < 100000; ++i) {
+    var array = [];
+    var offset = i & 3;
+    for (var j = 0; j < offset; ++j)
+        array.push(42);
+    var result = foo(array.concat([1, 2, 3, 4, 5, 6, 7, 8]), offset);
+    if (!arraycmp(result, [1, 2, 3, 4, 5, 6, 7, 8]))
+        throw "Error: bad result (1..8): " + result;
+}
+
+var result = foo([1, 2, 3, 4, 5, 6, 7], 0);
+if (!arraycmp(result, [1, 2, 3, 4, 5, 6, 7, void 0]))
+    throw "Error: bad result (1..7): " + result;
+var result = foo([1, 2, 3, 4, 5, 6], 0);
+if (!arraycmp(result, [1, 2, 3, 4, 5, 6, void 0, void 0]))
+    throw "Error: bad result (1..6): " + result;
+var result = foo([1, 2, 3, 4, 5], 0);
+if (!arraycmp(result, [1, 2, 3, 4, 5, void 0, void 0, void 0]))
+    throw "Error: bad result (1..5): " + result;
+var result = foo([1, 2, 3, 4], 0);
+if (!arraycmp(result, [1, 2, 3, 4, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..4): " + result;
+var result = foo([1, 2, 3], 0);
+if (!arraycmp(result, [1, 2, 3, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..3): " + result;
+var result = foo([1, 2], 0);
+if (!arraycmp(result, [1, 2, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..2): " + result;
+var result = foo([1], 0);
+if (!arraycmp(result, [1, void 0, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..1): " + result;
+var result = foo([], 0);
+if (!arraycmp(result, [void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..1): " + result;
diff --git a/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks-addition.js b/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks-addition.js
new file mode 100644
index 0000000..256e536
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks-addition.js
@@ -0,0 +1,50 @@
+function foo(a, i) {
+    return [a[i + 0], a[i + 1], a[i + 2], a[i + 3], a[i + 4], a[i + 5], a[i + 6], a[i + 7]];
+}
+
+noInline(foo);
+
+function arraycmp(a, b) {
+    if (a.length != b.length)
+        return false;
+    for (var i = 0; i < a.length; ++i) {
+        if (a[i] != b[i])
+            return false;
+    }
+    return true;
+}
+
+for (var i = 0; i < 100000; ++i) {
+    var array = [];
+    var offset = i & 3;
+    for (var j = 0; j < offset; ++j)
+        array.push(42);
+    var result = foo(array.concat([1, 2, 3, 4, 5, 6, 7, 8]), offset);
+    if (!arraycmp(result, [1, 2, 3, 4, 5, 6, 7, 8]))
+        throw "Error: bad result (1..8): " + result;
+}
+
+var result = foo([1, 2, 3, 4, 5, 6, 7], 0);
+if (!arraycmp(result, [1, 2, 3, 4, 5, 6, 7, void 0]))
+    throw "Error: bad result (1..7): " + result;
+var result = foo([1, 2, 3, 4, 5, 6], 0);
+if (!arraycmp(result, [1, 2, 3, 4, 5, 6, void 0, void 0]))
+    throw "Error: bad result (1..6): " + result;
+var result = foo([1, 2, 3, 4, 5], 0);
+if (!arraycmp(result, [1, 2, 3, 4, 5, void 0, void 0, void 0]))
+    throw "Error: bad result (1..5): " + result;
+var result = foo([1, 2, 3, 4], 0);
+if (!arraycmp(result, [1, 2, 3, 4, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..4): " + result;
+var result = foo([1, 2, 3], 0);
+if (!arraycmp(result, [1, 2, 3, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..3): " + result;
+var result = foo([1, 2], 0);
+if (!arraycmp(result, [1, 2, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..2): " + result;
+var result = foo([1], 0);
+if (!arraycmp(result, [1, void 0, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..1): " + result;
+var result = foo([], 0);
+if (!arraycmp(result, [void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..1): " + result;
diff --git a/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks-unchecked-addition.js b/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks-unchecked-addition.js
new file mode 100644
index 0000000..5ccb2be
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks-unchecked-addition.js
@@ -0,0 +1,50 @@
+function foo(a, i) {
+    return [a[(i + 0)|0], a[(i + 1)|0], a[(i + 2)|0], a[(i + 3)|0], a[(i + 4)|0], a[(i + 5)|0], a[(i + 6)|0], a[(i + 7)|0]];
+}
+
+noInline(foo);
+
+function arraycmp(a, b) {
+    if (a.length != b.length)
+        return false;
+    for (var i = 0; i < a.length; ++i) {
+        if (a[i] != b[i])
+            return false;
+    }
+    return true;
+}
+
+for (var i = 0; i < 100000; ++i) {
+    var array = [];
+    var offset = i & 3;
+    for (var j = 0; j < offset; ++j)
+        array.push(42);
+    var result = foo(array.concat([1, 2, 3, 4, 5, 6, 7, 8]), offset);
+    if (!arraycmp(result, [1, 2, 3, 4, 5, 6, 7, 8]))
+        throw "Error: bad result (1..8): " + result;
+}
+
+var result = foo([1, 2, 3, 4, 5, 6, 7], 0);
+if (!arraycmp(result, [1, 2, 3, 4, 5, 6, 7, void 0]))
+    throw "Error: bad result (1..7): " + result;
+var result = foo([1, 2, 3, 4, 5, 6], 0);
+if (!arraycmp(result, [1, 2, 3, 4, 5, 6, void 0, void 0]))
+    throw "Error: bad result (1..6): " + result;
+var result = foo([1, 2, 3, 4, 5], 0);
+if (!arraycmp(result, [1, 2, 3, 4, 5, void 0, void 0, void 0]))
+    throw "Error: bad result (1..5): " + result;
+var result = foo([1, 2, 3, 4], 0);
+if (!arraycmp(result, [1, 2, 3, 4, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..4): " + result;
+var result = foo([1, 2, 3], 0);
+if (!arraycmp(result, [1, 2, 3, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..3): " + result;
+var result = foo([1, 2], 0);
+if (!arraycmp(result, [1, 2, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..2): " + result;
+var result = foo([1], 0);
+if (!arraycmp(result, [1, void 0, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..1): " + result;
+var result = foo([], 0);
+if (!arraycmp(result, [void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..1): " + result;
diff --git a/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks.js b/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks.js
new file mode 100644
index 0000000..df0c7cd
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/redundant-array-bounds-checks.js
@@ -0,0 +1,46 @@
+function foo(a) {
+    return [a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]];
+}
+
+noInline(foo);
+
+function arraycmp(a, b) {
+    if (a.length != b.length)
+        return false;
+    for (var i = 0; i < a.length; ++i) {
+        if (a[i] != b[i])
+            return false;
+    }
+    return true;
+}
+
+for (var i = 0; i < 100000; ++i) {
+    var result = foo([1, 2, 3, 4, 5, 6, 7, 8]);
+    if (!arraycmp(result, [1, 2, 3, 4, 5, 6, 7, 8]))
+        throw "Error: bad result (1..8): " + result;
+}
+
+var result = foo([1, 2, 3, 4, 5, 6, 7]);
+if (!arraycmp(result, [1, 2, 3, 4, 5, 6, 7, void 0]))
+    throw "Error: bad result (1..7): " + result;
+var result = foo([1, 2, 3, 4, 5, 6]);
+if (!arraycmp(result, [1, 2, 3, 4, 5, 6, void 0, void 0]))
+    throw "Error: bad result (1..6): " + result;
+var result = foo([1, 2, 3, 4, 5]);
+if (!arraycmp(result, [1, 2, 3, 4, 5, void 0, void 0, void 0]))
+    throw "Error: bad result (1..5): " + result;
+var result = foo([1, 2, 3, 4]);
+if (!arraycmp(result, [1, 2, 3, 4, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..4): " + result;
+var result = foo([1, 2, 3]);
+if (!arraycmp(result, [1, 2, 3, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..3): " + result;
+var result = foo([1, 2]);
+if (!arraycmp(result, [1, 2, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..2): " + result;
+var result = foo([1]);
+if (!arraycmp(result, [1, void 0, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..1): " + result;
+var result = foo([]);
+if (!arraycmp(result, [void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0]))
+    throw "Error: bad result (1..1): " + result;
diff --git a/Source/JavaScriptCore/tests/stress/tricky-array-bounds-checks.js b/Source/JavaScriptCore/tests/stress/tricky-array-bounds-checks.js
new file mode 100644
index 0000000..28fe8ca
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/tricky-array-bounds-checks.js
@@ -0,0 +1,27 @@
+function foo(a, i, p) {
+    if (p || !DFGTrue())
+        return [a[(i - (DFGTrue() ? 2147483646 : 0)) | 0], a[i], a[(i + (DFGTrue() ? 2147483646 : 0)) | 0], DFGTrue()];
+    return [12];
+}
+
+noInline(foo);
+
+function arraycmp(a, b) {
+    if (a.length != b.length)
+        return false;
+    for (var i = 0; i < a.length; ++i) {
+        if (a[i] != b[i])
+            return false;
+    }
+    return true;
+}
+
+for (var i = 0; i < 100000; ++i) {
+    var result = foo([42], 0, false);
+    if (!arraycmp(result, [42, 42, 42, false]) && !arraycmp(result, [12]))
+        throw "Error: bad result for i = " + i + ": " + result;
+}
+
+var result = foo([1, 2, 3, 4, 5], -2147483646, true);
+if (!arraycmp(result, [5, void 0, void 0, false]))
+    throw "Error: bad result for trick: " + result;