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;