Source/JavaScriptCore:
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Apr 2016 22:13:16 +0000 (22:13 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Apr 2016 22:13:16 +0000 (22:13 +0000)
DFG and FTL should constant-fold RegExpExec, RegExpTest, and StringReplace
https://bugs.webkit.org/show_bug.cgi?id=155270

Reviewed by Saam Barati.

This enables constant-folding of RegExpExec, RegExpTest, and StringReplace.

It's now possible to run Yarr on the JIT threads. Since previous work on constant-folding
strings gave the DFG an API for reasoning about JSString constants in terms of
JIT-thread-local WTF::Strings, it's now super easy to just pass strings to Yarr and build IR
based on the results.

But RegExpExec is hard: the folded version still must allocate a RegExpMatchesArray. We must
use the same Structure that the code would have used or else we'll pollute the program's
inline caches. Also, RegExpMatchesArray.h|cpp will allocate the array and its named
properties in one go - we don't want to lose that optimization. So, this patch enables
MaterializeNewObject to allocate objects or arrays with any number of indexed or named
properties. Previously it could only handle objects (but not arrays) and named properties
(but not indexed ones).

This also adds a few minor things for setting the RegExpConstructor cached result.

This is about a 2x speed-up on microbenchmarks when we fold a match success and about a
8x speed-up when we fold a match failure. It's a 10% speed-up on Octane/regexp.

* JavaScriptCore.xcodeproj/project.pbxproj:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGInsertionSet.cpp:
(JSC::DFG::InsertionSet::insertSlow):
(JSC::DFG::InsertionSet::execute):
* dfg/DFGInsertionSet.h:
(JSC::DFG::InsertionSet::insertCheck):
* dfg/DFGLazyJSValue.cpp:
(JSC::DFG::LazyJSValue::tryGetString):
* dfg/DFGMayExit.cpp:
(JSC::DFG::mayExit):
* dfg/DFGNode.h:
(JSC::DFG::StackAccessData::flushedAt):
(JSC::DFG::OpInfo::OpInfo): Deleted.
* dfg/DFGNodeType.h:
* dfg/DFGObjectAllocationSinkingPhase.cpp:
* dfg/DFGObjectMaterializationData.cpp:
(JSC::DFG::ObjectMaterializationData::dump):
(JSC::DFG::PhantomPropertyValue::dump): Deleted.
(JSC::DFG::ObjectMaterializationData::oneWaySimilarityScore): Deleted.
(JSC::DFG::ObjectMaterializationData::similarityScore): Deleted.
* dfg/DFGObjectMaterializationData.h:
(JSC::DFG::PhantomPropertyValue::PhantomPropertyValue): Deleted.
(JSC::DFG::PhantomPropertyValue::operator==): Deleted.
* dfg/DFGOpInfo.h: Added.
(JSC::DFG::OpInfo::OpInfo):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGPromotedHeapLocation.cpp:
(WTF::printInternal):
* dfg/DFGPromotedHeapLocation.h:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::~SpeculativeJIT):
(JSC::DFG::SpeculativeJIT::emitAllocateRawObject):
(JSC::DFG::SpeculativeJIT::emitGetLength):
(JSC::DFG::SpeculativeJIT::compileLazyJSConstant):
(JSC::DFG::SpeculativeJIT::compileMaterializeNewObject):
(JSC::DFG::SpeculativeJIT::compileRecordRegExpCachedResult):
(JSC::DFG::SpeculativeJIT::emitAllocateJSArray): Deleted.
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::emitAllocateDestructibleObject):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStoreBarrierInsertionPhase.cpp:
* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::StrengthReductionPhase):
(JSC::DFG::StrengthReductionPhase::handleNode):
(JSC::DFG::StrengthReductionPhase::handleCommutativity):
(JSC::DFG::StrengthReductionPhase::executeInsertionSet):
* dfg/DFGValidate.cpp:
(JSC::DFG::Validate::validate):
(JSC::DFG::Validate::validateCPS):
* ftl/FTLAbstractHeapRepository.cpp:
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileNewArrayWithSize):
(JSC::FTL::DFG::LowerDFGToB3::compileMaterializeNewObject):
(JSC::FTL::DFG::LowerDFGToB3::compileMaterializeCreateActivation):
(JSC::FTL::DFG::LowerDFGToB3::compileSetRegExpObjectLastIndex):
(JSC::FTL::DFG::LowerDFGToB3::compileRecordRegExpCachedResult):
(JSC::FTL::DFG::LowerDFGToB3::didOverflowStack):
(JSC::FTL::DFG::LowerDFGToB3::storageForTransition):
(JSC::FTL::DFG::LowerDFGToB3::initializeArrayElements):
(JSC::FTL::DFG::LowerDFGToB3::allocatePropertyStorage):
(JSC::FTL::DFG::LowerDFGToB3::isNotCellOrMisc):
(JSC::FTL::DFG::LowerDFGToB3::unboxDouble):
* ftl/FTLOperations.cpp:
(JSC::FTL::operationPopulateObjectInOSR):
(JSC::FTL::operationNewObjectWithButterfly): Deleted.
* ftl/FTLOperations.h:
* inspector/ContentSearchUtilities.cpp:
* runtime/JSObject.h:
(JSC::JSObject::createRawObject):
(JSC::JSFinalObject::create):
* runtime/RegExp.cpp:
(JSC::RegExp::compile):
(JSC::RegExp::match):
(JSC::RegExp::matchConcurrently):
(JSC::RegExp::compileMatchOnly):
(JSC::RegExp::deleteCode):
* runtime/RegExp.h:
* runtime/RegExpCachedResult.h:
(JSC::RegExpCachedResult::offsetOfLastRegExp):
(JSC::RegExpCachedResult::offsetOfLastInput):
(JSC::RegExpCachedResult::offsetOfResult):
(JSC::RegExpCachedResult::offsetOfReified):
* runtime/RegExpConstructor.h:
(JSC::RegExpConstructor::offsetOfCachedResult):
* runtime/RegExpInlines.h:
(JSC::RegExp::hasCodeFor):
(JSC::RegExp::compileIfNecessary):
(JSC::RegExp::matchInline):
(JSC::RegExp::hasMatchOnlyCodeFor):
(JSC::RegExp::compileIfNecessaryMatchOnly):
* runtime/RegExpObjectInlines.h:
(JSC::RegExpObject::execInline):
* runtime/StringPrototype.cpp:
(JSC::substituteBackreferencesSlow):
(JSC::substituteBackreferencesInline):
(JSC::substituteBackreferences):
(JSC::StringRange::StringRange):
* runtime/StringPrototype.h:
* runtime/VM.h:
* tests/stress/simple-regexp-exec-folding-fail.js: Added.
(foo):
* tests/stress/simple-regexp-exec-folding.js: Added.
(foo):
* tests/stress/simple-regexp-test-folding-fail.js: Added.
(foo):
* tests/stress/simple-regexp-test-folding.js: Added.
(foo):
* yarr/RegularExpression.cpp:
* yarr/Yarr.h:
* yarr/YarrInterpreter.cpp:
(JSC::Yarr::Interpreter::interpret):
(JSC::Yarr::ByteCompiler::ByteCompiler):
(JSC::Yarr::ByteCompiler::compile):
(JSC::Yarr::ByteCompiler::checkInput):
(JSC::Yarr::byteCompile):
(JSC::Yarr::interpret):
* yarr/YarrInterpreter.h:
(JSC::Yarr::BytecodePattern::BytecodePattern):

Source/WTF:
DFG and FTL should constant-fold RegExpExec
https://bugs.webkit.org/show_bug.cgi?id=155270

Reviewed by Saam Barati.

Make executeInsertions() return the amount by which the vector increased in size. This is a
convenient feature that I use in DFG::InsertionSet.

* wtf/Insertion.h:
(WTF::executeInsertions):

LayoutTests:
DFG and FTL should constant-fold RegExpExec
https://bugs.webkit.org/show_bug.cgi?id=155270

Reviewed by Saam Barati.

* js/regress/script-tests/simple-regexp-exec-folding-fail.js: Added.
* js/regress/script-tests/simple-regexp-exec-folding.js: Added.
* js/regress/script-tests/simple-regexp-test-folding-fail.js: Added.
* js/regress/script-tests/simple-regexp-test-folding.js: Added.
* js/regress/simple-regexp-exec-folding-expected.txt: Added.
* js/regress/simple-regexp-exec-folding-fail-expected.txt: Added.
* js/regress/simple-regexp-exec-folding-fail.html: Added.
* js/regress/simple-regexp-exec-folding.html: Added.
* js/regress/simple-regexp-test-folding-expected.txt: Added.
* js/regress/simple-regexp-test-folding-fail-expected.txt: Added.
* js/regress/simple-regexp-test-folding-fail.html: Added.
* js/regress/simple-regexp-test-folding.html: Added.

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

69 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/script-tests/simple-regexp-exec-folding-fail.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/simple-regexp-exec-folding.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/simple-regexp-test-folding-fail.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/simple-regexp-test-folding.js [new file with mode: 0644]
LayoutTests/js/regress/simple-regexp-exec-folding-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/simple-regexp-exec-folding-fail-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/simple-regexp-exec-folding-fail.html [new file with mode: 0644]
LayoutTests/js/regress/simple-regexp-exec-folding.html [new file with mode: 0644]
LayoutTests/js/regress/simple-regexp-test-folding-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/simple-regexp-test-folding-fail-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/simple-regexp-test-folding-fail.html [new file with mode: 0644]
LayoutTests/js/regress/simple-regexp-test-folding.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGInsertionSet.cpp
Source/JavaScriptCore/dfg/DFGInsertionSet.h
Source/JavaScriptCore/dfg/DFGMayExit.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
Source/JavaScriptCore/dfg/DFGObjectMaterializationData.cpp
Source/JavaScriptCore/dfg/DFGObjectMaterializationData.h
Source/JavaScriptCore/dfg/DFGOpInfo.h [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.cpp
Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp
Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp
Source/JavaScriptCore/dfg/DFGValidate.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/ftl/FTLOperations.cpp
Source/JavaScriptCore/ftl/FTLOperations.h
Source/JavaScriptCore/inspector/ContentSearchUtilities.cpp
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/RegExp.cpp
Source/JavaScriptCore/runtime/RegExp.h
Source/JavaScriptCore/runtime/RegExpCachedResult.h
Source/JavaScriptCore/runtime/RegExpConstructor.h
Source/JavaScriptCore/runtime/RegExpInlines.h
Source/JavaScriptCore/runtime/RegExpObjectInlines.h
Source/JavaScriptCore/runtime/StringPrototype.cpp
Source/JavaScriptCore/runtime/StringPrototype.h
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/tests/stress/simple-regexp-exec-folding-fail.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/simple-regexp-exec-folding.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/simple-regexp-test-folding-fail.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/simple-regexp-test-folding.js [new file with mode: 0644]
Source/JavaScriptCore/yarr/RegularExpression.cpp
Source/JavaScriptCore/yarr/Yarr.h
Source/JavaScriptCore/yarr/YarrInterpreter.cpp
Source/JavaScriptCore/yarr/YarrInterpreter.h
Source/WTF/ChangeLog
Source/WTF/wtf/Insertion.h

index 88936af..c9ce984 100644 (file)
@@ -1,3 +1,23 @@
+2016-03-19  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG and FTL should constant-fold RegExpExec
+        https://bugs.webkit.org/show_bug.cgi?id=155270
+
+        Reviewed by Saam Barati.
+
+        * js/regress/script-tests/simple-regexp-exec-folding-fail.js: Added.
+        * js/regress/script-tests/simple-regexp-exec-folding.js: Added.
+        * js/regress/script-tests/simple-regexp-test-folding-fail.js: Added.
+        * js/regress/script-tests/simple-regexp-test-folding.js: Added.
+        * js/regress/simple-regexp-exec-folding-expected.txt: Added.
+        * js/regress/simple-regexp-exec-folding-fail-expected.txt: Added.
+        * js/regress/simple-regexp-exec-folding-fail.html: Added.
+        * js/regress/simple-regexp-exec-folding.html: Added.
+        * js/regress/simple-regexp-test-folding-expected.txt: Added.
+        * js/regress/simple-regexp-test-folding-fail-expected.txt: Added.
+        * js/regress/simple-regexp-test-folding-fail.html: Added.
+        * js/regress/simple-regexp-test-folding.html: Added.
+
 2016-04-04  Filip Pizlo  <fpizlo@apple.com>
 
         PolymorphicAccess should have a MegamorphicLoad case
diff --git a/LayoutTests/js/regress/script-tests/simple-regexp-exec-folding-fail.js b/LayoutTests/js/regress/script-tests/simple-regexp-exec-folding-fail.js
new file mode 100644 (file)
index 0000000..cbce611
--- /dev/null
@@ -0,0 +1,5 @@
+(function() {
+    for (var i = 0; i < 1000000; ++i)
+        /foo/.exec("bar");
+})();
+
diff --git a/LayoutTests/js/regress/script-tests/simple-regexp-exec-folding.js b/LayoutTests/js/regress/script-tests/simple-regexp-exec-folding.js
new file mode 100644 (file)
index 0000000..35f6906
--- /dev/null
@@ -0,0 +1,5 @@
+(function() {
+    for (var i = 0; i < 1000000; ++i)
+        /foo/.exec("foo");
+})();
+
diff --git a/LayoutTests/js/regress/script-tests/simple-regexp-test-folding-fail.js b/LayoutTests/js/regress/script-tests/simple-regexp-test-folding-fail.js
new file mode 100644 (file)
index 0000000..25d8495
--- /dev/null
@@ -0,0 +1,5 @@
+(function() {
+    for (var i = 0; i < 1000000; ++i)
+        /foo/.test("bar");
+})();
+
diff --git a/LayoutTests/js/regress/script-tests/simple-regexp-test-folding.js b/LayoutTests/js/regress/script-tests/simple-regexp-test-folding.js
new file mode 100644 (file)
index 0000000..f462f48
--- /dev/null
@@ -0,0 +1,5 @@
+(function() {
+    for (var i = 0; i < 1000000; ++i)
+        /foo/.test("foo");
+})();
+
diff --git a/LayoutTests/js/regress/simple-regexp-exec-folding-expected.txt b/LayoutTests/js/regress/simple-regexp-exec-folding-expected.txt
new file mode 100644 (file)
index 0000000..bfe4809
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/simple-regexp-exec-folding
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/simple-regexp-exec-folding-fail-expected.txt b/LayoutTests/js/regress/simple-regexp-exec-folding-fail-expected.txt
new file mode 100644 (file)
index 0000000..0a62b71
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/simple-regexp-exec-folding-fail
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/simple-regexp-exec-folding-fail.html b/LayoutTests/js/regress/simple-regexp-exec-folding-fail.html
new file mode 100644 (file)
index 0000000..cb6c4bc
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/simple-regexp-exec-folding-fail.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/simple-regexp-exec-folding.html b/LayoutTests/js/regress/simple-regexp-exec-folding.html
new file mode 100644 (file)
index 0000000..bd51288
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/simple-regexp-exec-folding.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/simple-regexp-test-folding-expected.txt b/LayoutTests/js/regress/simple-regexp-test-folding-expected.txt
new file mode 100644 (file)
index 0000000..7e42205
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/simple-regexp-test-folding
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/simple-regexp-test-folding-fail-expected.txt b/LayoutTests/js/regress/simple-regexp-test-folding-fail-expected.txt
new file mode 100644 (file)
index 0000000..95b2369
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/simple-regexp-test-folding-fail
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/simple-regexp-test-folding-fail.html b/LayoutTests/js/regress/simple-regexp-test-folding-fail.html
new file mode 100644 (file)
index 0000000..2c4e8f8
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/simple-regexp-test-folding-fail.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/simple-regexp-test-folding.html b/LayoutTests/js/regress/simple-regexp-test-folding.html
new file mode 100644 (file)
index 0000000..e3f6bf8
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/simple-regexp-test-folding.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 3293869..eae9a74 100644 (file)
@@ -1,3 +1,171 @@
+2016-03-19  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG and FTL should constant-fold RegExpExec, RegExpTest, and StringReplace
+        https://bugs.webkit.org/show_bug.cgi?id=155270
+
+        Reviewed by Saam Barati.
+
+        This enables constant-folding of RegExpExec, RegExpTest, and StringReplace.
+
+        It's now possible to run Yarr on the JIT threads. Since previous work on constant-folding
+        strings gave the DFG an API for reasoning about JSString constants in terms of
+        JIT-thread-local WTF::Strings, it's now super easy to just pass strings to Yarr and build IR
+        based on the results.
+
+        But RegExpExec is hard: the folded version still must allocate a RegExpMatchesArray. We must
+        use the same Structure that the code would have used or else we'll pollute the program's
+        inline caches. Also, RegExpMatchesArray.h|cpp will allocate the array and its named
+        properties in one go - we don't want to lose that optimization. So, this patch enables
+        MaterializeNewObject to allocate objects or arrays with any number of indexed or named
+        properties. Previously it could only handle objects (but not arrays) and named properties
+        (but not indexed ones).
+
+        This also adds a few minor things for setting the RegExpConstructor cached result.
+
+        This is about a 2x speed-up on microbenchmarks when we fold a match success and about a
+        8x speed-up when we fold a match failure. It's a 10% speed-up on Octane/regexp.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGGraph.cpp:
+        (JSC::DFG::Graph::dump):
+        * dfg/DFGInsertionSet.cpp:
+        (JSC::DFG::InsertionSet::insertSlow):
+        (JSC::DFG::InsertionSet::execute):
+        * dfg/DFGInsertionSet.h:
+        (JSC::DFG::InsertionSet::insertCheck):
+        * dfg/DFGLazyJSValue.cpp:
+        (JSC::DFG::LazyJSValue::tryGetString):
+        * dfg/DFGMayExit.cpp:
+        (JSC::DFG::mayExit):
+        * dfg/DFGNode.h:
+        (JSC::DFG::StackAccessData::flushedAt):
+        (JSC::DFG::OpInfo::OpInfo): Deleted.
+        * dfg/DFGNodeType.h:
+        * dfg/DFGObjectAllocationSinkingPhase.cpp:
+        * dfg/DFGObjectMaterializationData.cpp:
+        (JSC::DFG::ObjectMaterializationData::dump):
+        (JSC::DFG::PhantomPropertyValue::dump): Deleted.
+        (JSC::DFG::ObjectMaterializationData::oneWaySimilarityScore): Deleted.
+        (JSC::DFG::ObjectMaterializationData::similarityScore): Deleted.
+        * dfg/DFGObjectMaterializationData.h:
+        (JSC::DFG::PhantomPropertyValue::PhantomPropertyValue): Deleted.
+        (JSC::DFG::PhantomPropertyValue::operator==): Deleted.
+        * dfg/DFGOpInfo.h: Added.
+        (JSC::DFG::OpInfo::OpInfo):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGPromotedHeapLocation.cpp:
+        (WTF::printInternal):
+        * dfg/DFGPromotedHeapLocation.h:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::~SpeculativeJIT):
+        (JSC::DFG::SpeculativeJIT::emitAllocateRawObject):
+        (JSC::DFG::SpeculativeJIT::emitGetLength):
+        (JSC::DFG::SpeculativeJIT::compileLazyJSConstant):
+        (JSC::DFG::SpeculativeJIT::compileMaterializeNewObject):
+        (JSC::DFG::SpeculativeJIT::compileRecordRegExpCachedResult):
+        (JSC::DFG::SpeculativeJIT::emitAllocateJSArray): Deleted.
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::emitAllocateDestructibleObject):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStoreBarrierInsertionPhase.cpp:
+        * dfg/DFGStrengthReductionPhase.cpp:
+        (JSC::DFG::StrengthReductionPhase::StrengthReductionPhase):
+        (JSC::DFG::StrengthReductionPhase::handleNode):
+        (JSC::DFG::StrengthReductionPhase::handleCommutativity):
+        (JSC::DFG::StrengthReductionPhase::executeInsertionSet):
+        * dfg/DFGValidate.cpp:
+        (JSC::DFG::Validate::validate):
+        (JSC::DFG::Validate::validateCPS):
+        * ftl/FTLAbstractHeapRepository.cpp:
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileNewArrayWithSize):
+        (JSC::FTL::DFG::LowerDFGToB3::compileMaterializeNewObject):
+        (JSC::FTL::DFG::LowerDFGToB3::compileMaterializeCreateActivation):
+        (JSC::FTL::DFG::LowerDFGToB3::compileSetRegExpObjectLastIndex):
+        (JSC::FTL::DFG::LowerDFGToB3::compileRecordRegExpCachedResult):
+        (JSC::FTL::DFG::LowerDFGToB3::didOverflowStack):
+        (JSC::FTL::DFG::LowerDFGToB3::storageForTransition):
+        (JSC::FTL::DFG::LowerDFGToB3::initializeArrayElements):
+        (JSC::FTL::DFG::LowerDFGToB3::allocatePropertyStorage):
+        (JSC::FTL::DFG::LowerDFGToB3::isNotCellOrMisc):
+        (JSC::FTL::DFG::LowerDFGToB3::unboxDouble):
+        * ftl/FTLOperations.cpp:
+        (JSC::FTL::operationPopulateObjectInOSR):
+        (JSC::FTL::operationNewObjectWithButterfly): Deleted.
+        * ftl/FTLOperations.h:
+        * inspector/ContentSearchUtilities.cpp:
+        * runtime/JSObject.h:
+        (JSC::JSObject::createRawObject):
+        (JSC::JSFinalObject::create):
+        * runtime/RegExp.cpp:
+        (JSC::RegExp::compile):
+        (JSC::RegExp::match):
+        (JSC::RegExp::matchConcurrently):
+        (JSC::RegExp::compileMatchOnly):
+        (JSC::RegExp::deleteCode):
+        * runtime/RegExp.h:
+        * runtime/RegExpCachedResult.h:
+        (JSC::RegExpCachedResult::offsetOfLastRegExp):
+        (JSC::RegExpCachedResult::offsetOfLastInput):
+        (JSC::RegExpCachedResult::offsetOfResult):
+        (JSC::RegExpCachedResult::offsetOfReified):
+        * runtime/RegExpConstructor.h:
+        (JSC::RegExpConstructor::offsetOfCachedResult):
+        * runtime/RegExpInlines.h:
+        (JSC::RegExp::hasCodeFor):
+        (JSC::RegExp::compileIfNecessary):
+        (JSC::RegExp::matchInline):
+        (JSC::RegExp::hasMatchOnlyCodeFor):
+        (JSC::RegExp::compileIfNecessaryMatchOnly):
+        * runtime/RegExpObjectInlines.h:
+        (JSC::RegExpObject::execInline):
+        * runtime/StringPrototype.cpp:
+        (JSC::substituteBackreferencesSlow):
+        (JSC::substituteBackreferencesInline):
+        (JSC::substituteBackreferences):
+        (JSC::StringRange::StringRange):
+        * runtime/StringPrototype.h:
+        * runtime/VM.h:
+        * tests/stress/simple-regexp-exec-folding-fail.js: Added.
+        (foo):
+        * tests/stress/simple-regexp-exec-folding.js: Added.
+        (foo):
+        * tests/stress/simple-regexp-test-folding-fail.js: Added.
+        (foo):
+        * tests/stress/simple-regexp-test-folding.js: Added.
+        (foo):
+        * yarr/RegularExpression.cpp:
+        * yarr/Yarr.h:
+        * yarr/YarrInterpreter.cpp:
+        (JSC::Yarr::Interpreter::interpret):
+        (JSC::Yarr::ByteCompiler::ByteCompiler):
+        (JSC::Yarr::ByteCompiler::compile):
+        (JSC::Yarr::ByteCompiler::checkInput):
+        (JSC::Yarr::byteCompile):
+        (JSC::Yarr::interpret):
+        * yarr/YarrInterpreter.h:
+        (JSC::Yarr::BytecodePattern::BytecodePattern):
+
 2016-04-05  Keith Miller  <keith_miller@apple.com>
 
         We should support the ability to do a non-effectful getById
index 158a967..13c0326 100644 (file)
                0F7C11AD1BC3862C00C74CDB /* CopyBarrier.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F7C11AC1BC3862C00C74CDB /* CopyBarrier.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F7C39FB1C8F629300480151 /* RegExpInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F7C39FA1C8F629300480151 /* RegExpInlines.h */; };
                0F7C39FD1C8F659500480151 /* RegExpObjectInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F7C39FC1C8F659500480151 /* RegExpObjectInlines.h */; };
+               0F7C39FF1C90C55B00480151 /* DFGOpInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F7C39FE1C90C55B00480151 /* DFGOpInfo.h */; };
                0F8023EA1613832B00A0BA45 /* ByValInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8023E91613832300A0BA45 /* ByValInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F8335B71639C1E6001443B5 /* ArrayAllocationProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */; };
                0F8335B81639C1EA001443B5 /* ArrayAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F7C11AC1BC3862C00C74CDB /* CopyBarrier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopyBarrier.h; sourceTree = "<group>"; };
                0F7C39FA1C8F629300480151 /* RegExpInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegExpInlines.h; sourceTree = "<group>"; };
                0F7C39FC1C8F659500480151 /* RegExpObjectInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegExpObjectInlines.h; sourceTree = "<group>"; };
+               0F7C39FE1C90C55B00480151 /* DFGOpInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGOpInfo.h; path = dfg/DFGOpInfo.h; sourceTree = "<group>"; };
                0F8023E91613832300A0BA45 /* ByValInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ByValInfo.h; sourceTree = "<group>"; };
                0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayAllocationProfile.cpp; sourceTree = "<group>"; };
                0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayAllocationProfile.h; sourceTree = "<group>"; };
                                0F2B9CDD19D0BA7D00B1D1B5 /* DFGObjectMaterializationData.h */,
                                86EC9DBF1328DF82002B2AD7 /* DFGOperations.cpp */,
                                86EC9DC01328DF82002B2AD7 /* DFGOperations.h */,
+                               0F7C39FE1C90C55B00480151 /* DFGOpInfo.h */,
                                A7D89CEE17A0B8CC00773AD8 /* DFGOSRAvailabilityAnalysisPhase.cpp */,
                                A7D89CEF17A0B8CC00773AD8 /* DFGOSRAvailabilityAnalysisPhase.h */,
                                0FD82E52141DAEDE00179C94 /* DFGOSREntry.cpp */,
                                0F64EAF31C4ECD0600621E9B /* AirArgInlines.h in Headers */,
                                A5EA710519F6DE740098F5EC /* generate_objc_configuration_header.py in Headers */,
                                A5EA710619F6DE760098F5EC /* generate_objc_configuration_implementation.py in Headers */,
+                               0F7C39FF1C90C55B00480151 /* DFGOpInfo.h in Headers */,
                                A5EA710719F6DE780098F5EC /* generate_objc_protocol_type_conversions_header.py in Headers */,
                                0F338DF61BE93D550013C88F /* B3ConstrainedValue.h in Headers */,
                                A5EA710819F6DE7A0098F5EC /* generate_objc_frontend_dispatcher_implementation.py in Headers */,
index 35b7d52..f284d18 100644 (file)
@@ -1955,6 +1955,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
 
     case SetRegExpObjectLastIndex:
+    case RecordRegExpCachedResult:
         break;
         
     case GetFromArguments:
index 6a4eda2..98a1e2d 100644 (file)
@@ -917,6 +917,10 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         write(RegExpObject_lastIndex);
         def(HeapLocation(RegExpObjectLastIndexLoc, RegExpObject_lastIndex, node->child1()), LazyNode(node->child2().node()));
         return;
+
+    case RecordRegExpCachedResult:
+        write(RegExpState);
+        return;
         
     case GetFromArguments: {
         AbstractHeap heap(DirectArgumentsProperties, node->capturedArgumentsOffset().offset());
@@ -1080,7 +1084,9 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         if (node->child2().useKind() == RegExpObjectUse
             && node->child3().useKind() == StringUse) {
             read(RegExpState);
+            read(RegExpObject_lastIndex);
             write(RegExpState);
+            write(RegExpObject_lastIndex);
             return;
         }
         read(World);
@@ -1092,7 +1098,9 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
             && node->child2().useKind() == RegExpObjectUse
             && node->child3().useKind() == StringUse) {
             read(RegExpState);
+            read(RegExpObject_lastIndex);
             write(RegExpState);
+            write(RegExpObject_lastIndex);
             return;
         }
         read(World);
index f38b9f9..adc4ce5 100644 (file)
@@ -117,6 +117,7 @@ bool doesGC(Graph& graph, Node* node)
     case PutClosureVar:
     case GetRegExpObjectLastIndex:
     case SetRegExpObjectLastIndex:
+    case RecordRegExpCachedResult:
     case GetGlobalVar:
     case GetGlobalLexicalVariable:
     case PutGlobalVariable:
index 9612e18..577379b 100644 (file)
@@ -1300,6 +1300,7 @@ private:
         case StoreBarrier:
         case GetRegExpObjectLastIndex:
         case SetRegExpObjectLastIndex:
+        case RecordRegExpCachedResult:
             // These are just nodes that we don't currently expect to see during fixup.
             // If we ever wanted to insert them prior to fixup, then we just have to create
             // fixup rules for them.
index a4e8733..35f13f0 100644 (file)
@@ -315,6 +315,8 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node, DumpContext*
             out.print(anotherComma, pointerDumpInContext(freeze(m_codeBlock->constantBuffer(node->startConstant())[i]), context));
         out.print("]");
     }
+    if (node->hasLazyJSValue())
+        out.print(comma, node->lazyJSValue());
     if (node->hasIndexingType())
         out.print(comma, IndexingTypeDump(node->indexingType()));
     if (node->hasTypedArrayType())
index 8878d7c..1471250 100644 (file)
@@ -45,9 +45,9 @@ void InsertionSet::insertSlow(const Insertion& insertion)
     m_insertions.insert(0, insertion);
 }
 
-void InsertionSet::execute(BasicBlock* block)
+size_t InsertionSet::execute(BasicBlock* block)
 {
-    executeInsertions(*block, m_insertions);
+    return executeInsertions(*block, m_insertions);
 }
 
 } } // namespace JSC::DFG
index 57ff55f..644e36d 100644 (file)
@@ -129,7 +129,7 @@ public:
         return nullptr;
     }
     
-    void execute(BasicBlock* block);
+    size_t execute(BasicBlock* block);
 
 private:
     void insertSlow(const Insertion&);
index 4433b44..888ed64 100644 (file)
@@ -131,6 +131,7 @@ ExitMode mayExit(Graph& graph, Node* node)
     case StoreBarrier:
     case PutByOffset:
     case PutClosureVar:
+    case RecordRegExpCachedResult:
         break;
 
     case StrCat:
index 2257bfc..d22067a 100644 (file)
@@ -42,6 +42,7 @@
 #include "DFGNodeOrigin.h"
 #include "DFGNodeType.h"
 #include "DFGObjectMaterializationData.h"
+#include "DFGOpInfo.h"
 #include "DFGTransition.h"
 #include "DFGUseKind.h"
 #include "DFGVariableAccessData.h"
@@ -219,20 +220,6 @@ struct StackAccessData {
     FlushedAt flushedAt() { return FlushedAt(format, machineLocal); }
 };
 
-// This type used in passing an immediate argument to Node constructor;
-// distinguishes an immediate value (typically an index into a CodeBlock data structure - 
-// a constant index, argument, or identifier) from a Node*.
-struct OpInfo {
-    OpInfo() : m_value(0) { }
-    explicit OpInfo(int32_t value) : m_value(static_cast<uintptr_t>(value)) { }
-    explicit OpInfo(uint32_t value) : m_value(static_cast<uintptr_t>(value)) { }
-#if OS(DARWIN) || USE(JSVALUE64)
-    explicit OpInfo(size_t value) : m_value(static_cast<uintptr_t>(value)) { }
-#endif
-    explicit OpInfo(void* value) : m_value(reinterpret_cast<uintptr_t>(value)) { }
-    uintptr_t m_value;
-};
-
 // === Node ===
 //
 // Node represents a single operation in the data flow graph.
index f1e1a56..e3f7c45 100644 (file)
@@ -224,6 +224,7 @@ namespace JSC { namespace DFG {
     macro(VarInjectionWatchpoint, NodeMustGenerate) \
     macro(GetRegExpObjectLastIndex, NodeResultJS) \
     macro(SetRegExpObjectLastIndex, NodeMustGenerate) \
+    macro(RecordRegExpCachedResult, NodeMustGenerate | NodeHasVarArgs) \
     macro(CheckCell, NodeMustGenerate) \
     macro(CheckNotEmpty, NodeMustGenerate) \
     macro(CheckBadCell, NodeMustGenerate) \
index 36bb13f..f5f7c87 100644 (file)
@@ -1974,7 +1974,7 @@ private:
 
                 case NamedPropertyPLoc: {
                     ASSERT(location.base() == allocation.identifier());
-                    data.m_properties.append(PhantomPropertyValue(location.info()));
+                    data.m_properties.append(location.descriptor());
                     Node* value = resolve(block, location);
                     if (m_sinkCandidates.contains(value))
                         m_graph.m_varArgChildren.append(m_bottom);
@@ -2024,7 +2024,7 @@ private:
 
                 case ClosureVarPLoc: {
                     ASSERT(location.base() == allocation.identifier());
-                    data.m_properties.append(PhantomPropertyValue(location.info()));
+                    data.m_properties.append(location.descriptor());
                     Node* value = resolve(block, location);
                     if (m_sinkCandidates.contains(value))
                         m_graph.m_varArgChildren.append(m_bottom);
index 3abdbe6..baf145e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 namespace JSC { namespace DFG {
 
-void PhantomPropertyValue::dump(PrintStream& out) const
-{
-    out.print("id", m_identifierNumber);
-}
-
 void ObjectMaterializationData::dump(PrintStream& out) const
 {
     out.print("[", listDump(m_properties), "]");
 }
 
-float ObjectMaterializationData::oneWaySimilarityScore(
-    const ObjectMaterializationData& other) const
-{
-    unsigned numHits = 0;
-    for (PhantomPropertyValue value : m_properties) {
-        if (other.m_properties.contains(value))
-            numHits++;
-    }
-    return static_cast<float>(numHits) / static_cast<float>(m_properties.size());
-}
-
-float ObjectMaterializationData::similarityScore(const ObjectMaterializationData& other) const
-{
-    return std::min(oneWaySimilarityScore(other), other.oneWaySimilarityScore(*this));
-}
-
 } } // namespace JSC::DFG
 
 #endif // ENABLE(DFG_JIT)
index 1c4febe..8a9087c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,6 +28,7 @@
 
 #if ENABLE(DFG_JIT)
 
+#include "DFGPromotedHeapLocation.h"
 #include <limits.h>
 #include <wtf/MathExtras.h>
 #include <wtf/PrintStream.h>
 
 namespace JSC { namespace DFG {
 
-struct PhantomPropertyValue {
-    PhantomPropertyValue()
-        : m_identifierNumber(UINT_MAX)
-    {
-    }
-    
-    PhantomPropertyValue(unsigned identifierNumber)
-        : m_identifierNumber(identifierNumber)
-    {
-    }
-    
-    unsigned m_identifierNumber;
-    
-    bool operator==(const PhantomPropertyValue& other) const
-    {
-        return m_identifierNumber == other.m_identifierNumber;
-    }
-    
-    void dump(PrintStream&) const;
-};
-
 struct ObjectMaterializationData {
     // Determines the meaning of the passed nodes.
-    Vector<PhantomPropertyValue> m_properties;
+    Vector<PromotedLocationDescriptor> m_properties;
     
     void dump(PrintStream&) const;
-    
-    // The fraction of my properties that the other data has.
-    float oneWaySimilarityScore(const ObjectMaterializationData&) const;
-    
-    // The minimum of the two possible one-way scores.
-    float similarityScore(const ObjectMaterializationData&) const;
 };
 
 } } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/dfg/DFGOpInfo.h b/Source/JavaScriptCore/dfg/DFGOpInfo.h
new file mode 100644 (file)
index 0000000..f344782
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 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 DFGOpInfo_h
+#define DFGOpInfo_h
+
+#include <wtf/StdLibExtras.h>
+
+#if ENABLE(DFG_JIT)
+
+namespace JSC { namespace DFG {
+
+// This type used in passing an immediate argument to Node constructor;
+// distinguishes an immediate value (typically an index into a CodeBlock data structure - 
+// a constant index, argument, or identifier) from a Node*.
+struct OpInfo {
+    OpInfo() : m_value(0) { }
+    explicit OpInfo(int32_t value) : m_value(static_cast<uintptr_t>(value)) { }
+    explicit OpInfo(uint32_t value) : m_value(static_cast<uintptr_t>(value)) { }
+#if OS(DARWIN) || USE(JSVALUE64)
+    explicit OpInfo(size_t value) : m_value(static_cast<uintptr_t>(value)) { }
+#endif
+    explicit OpInfo(void* value) : m_value(reinterpret_cast<uintptr_t>(value)) { }
+    uintptr_t m_value;
+};
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGOpInfo_h
+
index be34f70..f4359cd 100644 (file)
@@ -1397,6 +1397,60 @@ size_t JIT_OPERATION operationDefaultHasInstance(ExecState* exec, JSCell* value,
     return 0;
 }
 
+char* JIT_OPERATION operationNewRawObject(ExecState* exec, Structure* structure, int32_t length)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    Butterfly* butterfly;
+    if (structure->outOfLineCapacity() || hasIndexedProperties(structure->indexingType())) {
+        IndexingHeader header;
+        header.setVectorLength(length);
+        header.setPublicLength(0);
+        
+        butterfly = Butterfly::create(
+            vm, nullptr, 0, structure->outOfLineCapacity(),
+            hasIndexedProperties(structure->indexingType()), header,
+            length * sizeof(EncodedJSValue));
+    } else
+        butterfly = nullptr;
+
+    JSObject* result = JSObject::createRawObject(exec, structure, butterfly);
+    result->butterfly(); // Ensure that the butterfly is in to-space.
+    return bitwise_cast<char*>(result);
+}
+
+JSCell* JIT_OPERATION operationNewObjectWithButterfly(ExecState* exec, Structure* structure)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    
+    Butterfly* butterfly = Butterfly::create(
+        vm, nullptr, 0, structure->outOfLineCapacity(), false, IndexingHeader(), 0);
+    
+    JSObject* result = JSObject::createRawObject(exec, structure, butterfly);
+    result->butterfly(); // Ensure that the butterfly is in to-space.
+    return result;
+}
+
+JSCell* JIT_OPERATION operationNewObjectWithButterflyWithIndexingHeaderAndVectorLength(ExecState* exec, Structure* structure, unsigned length)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    IndexingHeader header;
+    header.setVectorLength(length);
+    header.setPublicLength(0);
+    Butterfly* butterfly = Butterfly::create(
+        vm, nullptr, 0, structure->outOfLineCapacity(), true, header,
+        sizeof(EncodedJSValue) * length);
+
+    // Paradoxically this may allocate a JSArray. That's totally cool.
+    JSObject* result = JSObject::createRawObject(exec, structure, butterfly);
+    result->butterfly(); // Ensure that the butterfly is in to-space.
+    return result;
+}
+
 void JIT_OPERATION operationProcessTypeProfilerLogDFG(ExecState* exec) 
 {
     exec->vm().typeProfilerLog()->processLogEntries(ASCIILiteral("Log Full, called from inside DFG."));
index 1939a30..6e40caf 100644 (file)
@@ -155,6 +155,10 @@ int64_t JIT_OPERATION operationConvertDoubleToInt52(double);
 
 size_t JIT_OPERATION operationDefaultHasInstance(ExecState*, JSCell* value, JSCell* proto);
 
+char* JIT_OPERATION operationNewRawObject(ExecState*, Structure*, int32_t) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationNewObjectWithButterfly(ExecState*, Structure*) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationNewObjectWithButterflyWithIndexingHeaderAndVectorLength(ExecState*, Structure*, unsigned length) WTF_INTERNAL;
+
 void JIT_OPERATION operationProcessTypeProfilerLogDFG(ExecState*) WTF_INTERNAL;
 
 void JIT_OPERATION debugOperationPrintSpeculationFailure(ExecState*, void*, void*) WTF_INTERNAL;
index df4d507..4d526ee 100644 (file)
@@ -668,6 +668,7 @@ private:
         case GetStack:
         case GetRegExpObjectLastIndex:
         case SetRegExpObjectLastIndex:
+        case RecordRegExpCachedResult:
         case LazyJSConstant: {
             // This node should never be visible at this stage of compilation. It is
             // inserted by fixup(), which follows this phase.
index 24f6977..3b0f752 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -74,6 +74,10 @@ void printInternal(PrintStream& out, PromotedLocationKind kind)
     case NamedPropertyPLoc:
         out.print("NamedPropertyPLoc");
         return;
+
+    case IndexedPropertyPLoc:
+        out.print("IndexedPropertyPLoc");
+        return;
         
     case ArgumentPLoc:
         out.print("ArgumentPLoc");
@@ -102,6 +106,14 @@ void printInternal(PrintStream& out, PromotedLocationKind kind)
     case ClosureVarPLoc:
         out.print("ClosureVarPLoc");
         return;
+
+    case PublicLengthPLoc:
+        out.print("PublicLengthPLoc");
+        return;
+
+    case VectorLengthPLoc:
+        out.print("VectorLengthPLoc");
+        return;
     }
     
     RELEASE_ASSERT_NOT_REACHED();
index b4e3c2b..f057dea 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #if ENABLE(DFG_JIT)
 
-#include "DFGNode.h"
+#include "DFGEdge.h"
+#include "DFGNodeOrigin.h"
+#include "DFGOpInfo.h"
+#include <wtf/HashTable.h>
 #include <wtf/PrintStream.h>
 
 namespace JSC { namespace DFG {
 
+struct Node;
+
+// Promoted locations are like heap locations but are meant to be more precise. A heap location is
+// applicable to CSE scenarios, where it makes sense to speak of a location very abstractly. A
+// promoted heap location is for cases where we speak of a specific object and the compiler knows
+// this object's identity - for example, the object allocation has been eliminated and we turned the
+// fields into local variables. Because these two cases have subtly different needs, we use subtly
+// different structures. One of the really significant differences is that promoted locations can be
+// spoken of using either a descriptor which does not refer to any Node*'s or with a heap location,
+// which is a descriptor with a Node* base.
+
 enum PromotedLocationKind {
     InvalidPromotedLocationKind,
     
-    StructurePLoc,
+    ActivationScopePLoc,
     ActivationSymbolTablePLoc,
-    NamedPropertyPLoc,
-    ArgumentPLoc,
     ArgumentCountPLoc,
+    ArgumentPLoc,
     ArgumentsCalleePLoc,
-
-    FunctionExecutablePLoc,
+    ClosureVarPLoc,
     FunctionActivationPLoc,
-    ActivationScopePLoc,
-    ClosureVarPLoc
+    FunctionExecutablePLoc,
+    IndexedPropertyPLoc,
+    NamedPropertyPLoc,
+    PublicLengthPLoc,
+    StructurePLoc,
+    VectorLengthPLoc
 };
 
 class PromotedLocationDescriptor {
index 8077b85..be1a75c 100644 (file)
@@ -336,6 +336,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case StringReplace:
     case GetRegExpObjectLastIndex:
     case SetRegExpObjectLastIndex:
+    case RecordRegExpCachedResult:
         return true;
 
     case BottomValue:
index 3ad03dc..7b297f5 100644 (file)
@@ -52,6 +52,7 @@
 #include "JSGeneratorFunction.h"
 #include "JSLexicalEnvironment.h"
 #include "LinkBuffer.h"
+#include "RegExpConstructor.h"
 #include "ScopedArguments.h"
 #include "ScratchRegisterAllocator.h"
 #include "WriteBarrierBuffer.h"
@@ -77,27 +78,54 @@ SpeculativeJIT::~SpeculativeJIT()
 {
 }
 
-void SpeculativeJIT::emitAllocateJSArray(GPRReg resultGPR, Structure* structure, GPRReg storageGPR, unsigned numElements)
+void SpeculativeJIT::emitAllocateRawObject(GPRReg resultGPR, Structure* structure, GPRReg storageGPR, unsigned numElements, unsigned vectorLength)
 {
-    ASSERT(hasUndecided(structure->indexingType()) || hasInt32(structure->indexingType()) || hasDouble(structure->indexingType()) || hasContiguous(structure->indexingType()));
+    IndexingType indexingType = structure->indexingType();
+    bool hasIndexingHeader = hasIndexedProperties(indexingType);
+
+    unsigned inlineCapacity = structure->inlineCapacity();
+    unsigned outOfLineCapacity = structure->outOfLineCapacity();
     
     GPRTemporary scratch(this);
     GPRTemporary scratch2(this);
     GPRReg scratchGPR = scratch.gpr();
     GPRReg scratch2GPR = scratch2.gpr();
-    
-    unsigned vectorLength = std::max(BASE_VECTOR_LEN, numElements);
+
+    ASSERT(vectorLength >= numElements);
+    vectorLength = std::max(BASE_VECTOR_LEN, vectorLength);
     
     JITCompiler::JumpList slowCases;
-    
-    slowCases.append(
-        emitAllocateBasicStorage(TrustedImm32(vectorLength * sizeof(JSValue) + sizeof(IndexingHeader)), storageGPR));
-    m_jit.subPtr(TrustedImm32(vectorLength * sizeof(JSValue)), storageGPR);
-    emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(structure), storageGPR, scratchGPR, scratch2GPR, slowCases);
-    
-    m_jit.store32(TrustedImm32(numElements), MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
-    m_jit.store32(TrustedImm32(vectorLength), MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
-    
+
+    size_t size = 0;
+    if (hasIndexingHeader)
+        size += vectorLength * sizeof(JSValue) + sizeof(IndexingHeader);
+    size += outOfLineCapacity * sizeof(JSValue);
+
+    if (size) {
+        slowCases.append(
+            emitAllocateBasicStorage(TrustedImm32(size), storageGPR));
+        if (hasIndexingHeader)
+            m_jit.subPtr(TrustedImm32(vectorLength * sizeof(JSValue)), storageGPR);
+        else
+            m_jit.addPtr(TrustedImm32(sizeof(IndexingHeader)), storageGPR);
+    } else
+        m_jit.move(TrustedImmPtr(0), storageGPR);
+
+    size_t allocationSize = JSFinalObject::allocationSize(inlineCapacity);
+    MarkedAllocator* allocatorPtr = &m_jit.vm()->heap.allocatorForObjectWithoutDestructor(allocationSize);
+    m_jit.move(TrustedImmPtr(allocatorPtr), scratchGPR);
+    emitAllocateJSObject(resultGPR, scratchGPR, TrustedImmPtr(structure), storageGPR, scratch2GPR, slowCases);
+
+    if (hasIndexingHeader)
+        m_jit.store32(TrustedImm32(vectorLength), MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
+
+    // I want a slow path that also loads out the storage pointer, and that's
+    // what this custom CallArrayAllocatorSlowPathGenerator gives me. It's a lot
+    // of work for a very small piece of functionality. :-/
+    addSlowPathGenerator(std::make_unique<CallArrayAllocatorSlowPathGenerator>(
+        slowCases, this, operationNewRawObject, resultGPR, storageGPR,
+        structure, vectorLength));
+
     if (hasDouble(structure->indexingType()) && numElements < vectorLength) {
 #if USE(JSVALUE64)
         m_jit.move(TrustedImm64(bitwise_cast<int64_t>(PNaN)), scratchGPR);
@@ -113,12 +141,8 @@ void SpeculativeJIT::emitAllocateJSArray(GPRReg resultGPR, Structure* structure,
 #endif
     }
     
-    // I want a slow path that also loads out the storage pointer, and that's
-    // what this custom CallArrayAllocatorSlowPathGenerator gives me. It's a lot
-    // of work for a very small piece of functionality. :-/
-    addSlowPathGenerator(std::make_unique<CallArrayAllocatorSlowPathGenerator>(
-        slowCases, this, operationNewArrayWithSize, resultGPR, storageGPR,
-        structure, numElements));
+    if (hasIndexingHeader)
+        m_jit.store32(TrustedImm32(numElements), MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
 }
 
 void SpeculativeJIT::emitGetLength(InlineCallFrame* inlineCallFrame, GPRReg lengthGPR, bool includeThis)
@@ -7653,6 +7677,125 @@ void SpeculativeJIT::compileLazyJSConstant(Node* node)
     jsValueResult(resultRegs, node);
 }
 
+void SpeculativeJIT::compileMaterializeNewObject(Node* node)
+{
+    Structure* structure = node->structureSet()[0];
+    ASSERT(m_jit.graph().varArgChild(node, 0)->dynamicCastConstant<Structure*>() == structure);
+
+    ObjectMaterializationData& data = node->objectMaterializationData();
+        
+    IndexingType indexingType = structure->indexingType();
+    bool hasIndexingHeader = hasIndexedProperties(indexingType);
+    int32_t publicLength = 0;
+    int32_t vectorLength = 0;
+
+    if (hasIndexingHeader) {
+        for (unsigned i = data.m_properties.size(); i--;) {
+            Edge edge = m_jit.graph().varArgChild(node, 1 + i);
+            switch (data.m_properties[i].kind()) {
+            case PublicLengthPLoc:
+                publicLength = edge->asInt32();
+                break;
+            case VectorLengthPLoc:
+                vectorLength = edge->asInt32();
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+    GPRTemporary result(this);
+    GPRTemporary storage(this);
+    GPRReg resultGPR = result.gpr();
+    GPRReg storageGPR = storage.gpr();
+    
+    emitAllocateRawObject(resultGPR, structure, storageGPR, 0, vectorLength);
+    
+    m_jit.store32(
+        JITCompiler::TrustedImm32(publicLength),
+        JITCompiler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
+
+    for (unsigned i = data.m_properties.size(); i--;) {
+        Edge edge = m_jit.graph().varArgChild(node, 1 + i);
+        PromotedLocationDescriptor descriptor = data.m_properties[i];
+        switch (descriptor.kind()) {
+        case IndexedPropertyPLoc: {
+            JSValueOperand value(this, edge);
+            m_jit.storeValue(
+                value.jsValueRegs(),
+                JITCompiler::Address(storageGPR, sizeof(EncodedJSValue) * descriptor.info()));
+            break;
+        }
+
+        case NamedPropertyPLoc: {
+            StringImpl* uid = m_jit.graph().identifiers()[descriptor.info()];
+            for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) {
+                if (uid != entry.key)
+                    continue;
+
+                JSValueOperand value(this, edge);
+                GPRReg baseGPR = isInlineOffset(entry.offset) ? resultGPR : storageGPR;
+                m_jit.storeValue(
+                    value.jsValueRegs(),
+                    JITCompiler::Address(baseGPR, offsetRelativeToBase(entry.offset)));
+            }
+            break;
+        }
+            
+        default:
+            break;
+        }
+    }
+
+    cellResult(resultGPR, node);
+}
+
+void SpeculativeJIT::compileRecordRegExpCachedResult(Node* node)
+{
+    Edge constructorEdge = m_jit.graph().varArgChild(node, 0);
+    Edge regExpEdge = m_jit.graph().varArgChild(node, 1);
+    Edge stringEdge = m_jit.graph().varArgChild(node, 2);
+    Edge startEdge = m_jit.graph().varArgChild(node, 3);
+    Edge endEdge = m_jit.graph().varArgChild(node, 4);
+
+    SpeculateCellOperand constructor(this, constructorEdge);
+    SpeculateCellOperand regExp(this, regExpEdge);
+    SpeculateCellOperand string(this, stringEdge);
+    SpeculateInt32Operand start(this, startEdge);
+    SpeculateInt32Operand end(this, endEdge);
+
+    GPRReg constructorGPR = constructor.gpr();
+    GPRReg regExpGPR = regExp.gpr();
+    GPRReg stringGPR = string.gpr();
+    GPRReg startGPR = start.gpr();
+    GPRReg endGPR = end.gpr();
+
+    ptrdiff_t offset = RegExpConstructor::offsetOfCachedResult();
+
+    m_jit.storePtr(
+        regExpGPR,
+        JITCompiler::Address(constructorGPR, offset + RegExpCachedResult::offsetOfLastRegExp()));
+    m_jit.storePtr(
+        stringGPR,
+        JITCompiler::Address(constructorGPR, offset + RegExpCachedResult::offsetOfLastInput()));
+    m_jit.store32(
+        startGPR,
+        JITCompiler::Address(
+            constructorGPR,
+            offset + RegExpCachedResult::offsetOfResult() + OBJECT_OFFSETOF(MatchResult, start)));
+    m_jit.store32(
+        endGPR,
+        JITCompiler::Address(
+            constructorGPR,
+            offset + RegExpCachedResult::offsetOfResult() + OBJECT_OFFSETOF(MatchResult, end)));
+    m_jit.store8(
+        TrustedImm32(0),
+        JITCompiler::Address(constructorGPR, offset + RegExpCachedResult::offsetOfReified()));
+
+    noResult(node);
+}
+
 } } // namespace JSC::DFG
 
 #endif
index 3ad9175..8409f65 100644 (file)
@@ -2410,6 +2410,8 @@ public:
     void compileGetRegExpObjectLastIndex(Node*);
     void compileSetRegExpObjectLastIndex(Node*);
     void compileLazyJSConstant(Node*);
+    void compileMaterializeNewObject(Node*);
+    void compileRecordRegExpCachedResult(Node*);
     
     void moveTrueTo(GPRReg);
     void moveFalseTo(GPRReg);
@@ -2525,7 +2527,7 @@ public:
         m_jit.storePtr(TrustedImmPtr(structure->classInfo()), MacroAssembler::Address(resultGPR, JSDestructibleObject::classInfoOffset()));
     }
 
-    void emitAllocateJSArray(GPRReg resultGPR, Structure*, GPRReg storageGPR, unsigned numElements);
+    void emitAllocateRawObject(GPRReg resultGPR, Structure*, GPRReg storageGPR, unsigned numElements, unsigned vectorLength);
     
     void emitGetLength(InlineCallFrame*, GPRReg lengthGPR, bool includeThis = false);
     void emitGetLength(CodeOrigin, GPRReg lengthGPR, bool includeThis = false);
index 9814c20..5a7766e 100644 (file)
@@ -3048,6 +3048,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
         
+    case RecordRegExpCachedResult: {
+        compileRecordRegExpCachedResult(node);
+        break;
+    }
+        
     case ArrayPush: {
         ASSERT(node->arrayMode().isJSArray());
         
@@ -3493,7 +3498,7 @@ void SpeculativeJIT::compile(Node* node)
             GPRReg resultGPR = result.gpr();
             GPRReg storageGPR = storage.gpr();
 
-            emitAllocateJSArray(resultGPR, structure, storageGPR, numElements);
+            emitAllocateRawObject(resultGPR, structure, storageGPR, numElements, numElements);
             
             // At this point, one way or another, resultGPR and storageGPR have pointers to
             // the JSArray and the Butterfly, respectively.
@@ -3729,7 +3734,7 @@ void SpeculativeJIT::compile(Node* node)
             GPRReg resultGPR = result.gpr();
             GPRReg storageGPR = storage.gpr();
 
-            emitAllocateJSArray(resultGPR, globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType), storageGPR, numElements);
+            emitAllocateRawObject(resultGPR, globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType), storageGPR, numElements, numElements);
             
             if (node->indexingType() == ArrayWithDouble) {
                 JSValue* data = m_jit.codeBlock()->constantBuffer(node->startConstant());
@@ -4980,7 +4985,10 @@ void SpeculativeJIT::compile(Node* node)
         noResult(node);
         break;
         
-        
+    case MaterializeNewObject:
+        compileMaterializeNewObject(node);
+        break;
+
     case Unreachable:
         RELEASE_ASSERT_NOT_REACHED();
         break;
@@ -5007,7 +5015,6 @@ void SpeculativeJIT::compile(Node* node)
     case PhantomCreateActivation:
     case PutHint:
     case CheckStructureImmediate:
-    case MaterializeNewObject:
     case MaterializeCreateActivation:
     case PutStack:
     case KillStack:
index 5e54f5f..772e2ba 100644 (file)
@@ -3188,6 +3188,11 @@ void SpeculativeJIT::compile(Node* node)
         compileSetRegExpObjectLastIndex(node);
         break;
     }
+
+    case RecordRegExpCachedResult: {
+        compileRecordRegExpCachedResult(node);
+        break;
+    }
         
     case ArrayPush: {
         ASSERT(node->arrayMode().isJSArray());
@@ -3559,7 +3564,7 @@ void SpeculativeJIT::compile(Node* node)
             GPRReg resultGPR = result.gpr();
             GPRReg storageGPR = storage.gpr();
 
-            emitAllocateJSArray(resultGPR, structure, storageGPR, numElements);
+            emitAllocateRawObject(resultGPR, structure, storageGPR, numElements, numElements);
             
             // At this point, one way or another, resultGPR and storageGPR have pointers to
             // the JSArray and the Butterfly, respectively.
@@ -3795,7 +3800,7 @@ void SpeculativeJIT::compile(Node* node)
             GPRReg resultGPR = result.gpr();
             GPRReg storageGPR = storage.gpr();
 
-            emitAllocateJSArray(resultGPR, globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType), storageGPR, numElements);
+            emitAllocateRawObject(resultGPR, globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType), storageGPR, numElements, numElements);
             
             DFG_ASSERT(m_jit.graph(), node, indexingType & IsArray);
             JSValue* data = m_jit.codeBlock()->constantBuffer(node->startConstant());
@@ -4989,6 +4994,10 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case MaterializeNewObject:
+        compileMaterializeNewObject(node);
+        break;
+
 #if ENABLE(FTL_JIT)        
     case CheckTierUpInLoop: {
         MacroAssembler::Jump done = m_jit.branchAdd32(
@@ -5079,7 +5088,6 @@ void SpeculativeJIT::compile(Node* node)
     case GetMyArgumentByVal:
     case PutHint:
     case CheckStructureImmediate:
-    case MaterializeNewObject:
     case MaterializeCreateActivation:
     case PutStack:
     case KillStack:
index 5824734..99aa81f 100644 (file)
@@ -253,11 +253,19 @@ private:
                 break;
             }
                 
+            case PutById:
+            case PutByIdFlush:
+            case PutByIdDirect:
             case PutStructure: {
                 considerBarrier(m_node->child1());
                 break;
             }
-                
+
+            case RecordRegExpCachedResult: {
+                considerBarrier(m_graph.varArgChild(m_node, 0));
+                break;
+            }
+
             case PutClosureVar:
             case PutToArguments:
             case SetRegExpObjectLastIndex:
@@ -266,13 +274,6 @@ private:
                 break;
             }
                 
-            case PutById:
-            case PutByIdFlush:
-            case PutByIdDirect: {
-                considerBarrier(m_node->child1());
-                break;
-            }
-
             case PutByOffset: {
                 considerBarrier(m_node->child2(), m_node->child3());
                 break;
index 84add10..49a92c5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "DFGPredictionPropagationPhase.h"
 #include "DFGVariableAccessDataDump.h"
 #include "JSCInlines.h"
+#include "RegExpConstructor.h"
+#include "StringPrototype.h"
 #include <cstdlib>
 
 namespace JSC { namespace DFG {
 
 class StrengthReductionPhase : public Phase {
+    static const bool verbose = false;
+    
 public:
     StrengthReductionPhase(Graph& graph)
         : Phase(graph, "strength reduction")
@@ -331,11 +335,368 @@ private:
                 if (!!string) {
                     m_graph.convertToConstant(m_node, jsNumber(string.length()));
                     m_changed = true;
+                    break;
                 }
             }
             break;
         }
 
+        case GetGlobalObject: {
+            if (JSObject* object = m_node->child1()->dynamicCastConstant<JSObject*>()) {
+                m_graph.convertToConstant(m_node, object->globalObject());
+                m_changed = true;
+                break;
+            }
+            break;
+        }
+
+        case RegExpExec:
+        case RegExpTest: {
+            JSGlobalObject* globalObject = m_node->child1()->dynamicCastConstant<JSGlobalObject*>();
+            if (!globalObject) {
+                if (verbose)
+                    dataLog("Giving up because no global object.\n");
+                break;
+            }
+
+            if (globalObject->isHavingABadTime()) {
+                if (verbose)
+                    dataLog("Giving up because bad time.\n");
+                break;
+            }
+
+            Node* regExpObjectNode = m_node->child2().node();
+            RegExp* regExp;
+            if (RegExpObject* regExpObject = regExpObjectNode->dynamicCastConstant<RegExpObject*>())
+                regExp = regExpObject->regExp();
+            else if (regExpObjectNode->op() == NewRegexp)
+                regExp = codeBlock()->regexp(regExpObjectNode->regexpIndex());
+            else {
+                if (verbose)
+                    dataLog("Giving up because the regexp is unknown.\n");
+                break;
+            }
+
+            Node* stringNode = m_node->child3().node();
+            
+            // NOTE: This mostly already protects us from having the compiler execute a regexp
+            // operation on a ginormous string by preventing us from getting our hands on ginormous
+            // strings in the first place.
+            String string = m_node->child3()->tryGetString(m_graph);
+            if (!string) {
+                if (verbose)
+                    dataLog("Giving up because the string is unknown.\n");
+                break;
+            }
+
+            FrozenValue* regExpFrozenValue = m_graph.freeze(regExp);
+
+            // Refuse to do things with regular expressions that have a ginormous number of
+            // subpatterns.
+            unsigned ginormousNumberOfSubPatterns = 1000;
+            if (regExp->numSubpatterns() > ginormousNumberOfSubPatterns) {
+                if (verbose)
+                    dataLog("Giving up because of pattern limit.\n");
+                break;
+            }
+
+            unsigned lastIndex;
+            if (regExp->globalOrSticky()) {
+                // This will only work if we can prove what the value of lastIndex is. To do this
+                // safely, we need to execute the insertion set so that we see any previous strength
+                // reductions. This is needed for soundness since otherwise the effectfulness of any
+                // previous strength reductions would be invisible to us.
+                executeInsertionSet();
+                lastIndex = UINT_MAX;
+                for (unsigned otherNodeIndex = m_nodeIndex; otherNodeIndex--;) {
+                    Node* otherNode = m_block->at(otherNodeIndex);
+                    if (otherNode == regExpObjectNode) {
+                        lastIndex = 0;
+                        break;
+                    }
+                    if (otherNode->op() == SetRegExpObjectLastIndex
+                        && otherNode->child1() == regExpObjectNode
+                        && otherNode->child2()->isInt32Constant()
+                        && otherNode->child2()->asInt32() >= 0) {
+                        lastIndex = static_cast<unsigned>(otherNode->child2()->asInt32());
+                        break;
+                    }
+                    if (writesOverlap(m_graph, otherNode, RegExpObject_lastIndex))
+                        break;
+                }
+                if (lastIndex == UINT_MAX) {
+                    if (verbose)
+                        dataLog("Giving up because the last index is not known.\n");
+                    break;
+                }
+            } else
+                lastIndex = 0;
+
+            m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint());
+            
+            Structure* structure = globalObject->regExpMatchesArrayStructure();
+            if (structure->indexingType() != ArrayWithContiguous) {
+                // This is further protection against a race with haveABadTime.
+                if (verbose)
+                    dataLog("Giving up because the structure has the wrong indexing type.\n");
+                break;
+            }
+            m_graph.registerStructure(structure);
+
+            RegExpConstructor* constructor = globalObject->regExpConstructor();
+            FrozenValue* constructorFrozenValue = m_graph.freeze(constructor);
+
+            MatchResult result;
+            Vector<int, 32> ovector;
+            // We have to call the kind of match function that the main thread would have called.
+            // Otherwise, we might not have the desired Yarr code compiled, and the match will fail.
+            if (m_node->op() == RegExpExec) {
+                int position;
+                if (!regExp->matchConcurrently(vm(), string, lastIndex, position, ovector)) {
+                    if (verbose)
+                        dataLog("Giving up because match failed.\n");
+                    break;
+                }
+                result.start = position;
+                result.end = ovector[1];
+            } else {
+                if (!regExp->matchConcurrently(vm(), string, lastIndex, result)) {
+                    if (verbose)
+                        dataLog("Giving up because match failed.\n");
+                    break;
+                }
+            }
+
+            // We've constant-folded the regexp. Now we're committed to replacing RegExpExec/Test.
+
+            m_changed = true;
+
+            NodeOrigin origin = m_node->origin;
+
+            m_insertionSet.insertNode(
+                m_nodeIndex, SpecNone, Check, origin, m_node->children.justChecks());
+
+            if (m_node->op() == RegExpExec) {
+                if (result) {
+                    StructureSet* structureSet = m_graph.addStructureSet(structure);
+
+                    // Create an array modeling the JS array that we will try to allocate. This is
+                    // basically createRegExpMatchesArray but over C++ strings instead of JSStrings.
+                    Vector<String> resultArray;
+                    resultArray.append(string.substring(result.start, result.end - result.start));
+                    for (unsigned i = 1; i <= regExp->numSubpatterns(); ++i) {
+                        int start = ovector[2 * i];
+                        if (start >= 0)
+                            resultArray.append(string.substring(start, ovector[2 * i + 1] - start));
+                        else
+                            resultArray.append(String());
+                    }
+
+                    unsigned publicLength = resultArray.size();
+                    unsigned vectorLength = std::max(BASE_VECTOR_LEN, publicLength);
+
+                    UniquedStringImpl* indexUID = vm().propertyNames->index.impl();
+                    UniquedStringImpl* inputUID = vm().propertyNames->input.impl();
+                    unsigned indexIndex = m_graph.identifiers().ensure(indexUID);
+                    unsigned inputIndex = m_graph.identifiers().ensure(inputUID);
+
+                    unsigned firstChild = m_graph.m_varArgChildren.size();
+                    m_graph.m_varArgChildren.append(
+                        m_insertionSet.insertConstantForUse(
+                            m_nodeIndex, origin, structure, KnownCellUse));
+                    ObjectMaterializationData* data = m_graph.m_objectMaterializationData.add();
+            
+                    m_graph.m_varArgChildren.append(
+                        m_insertionSet.insertConstantForUse(
+                            m_nodeIndex, origin, jsNumber(publicLength), KnownInt32Use));
+                    data->m_properties.append(PublicLengthPLoc);
+            
+                    m_graph.m_varArgChildren.append(
+                        m_insertionSet.insertConstantForUse(
+                            m_nodeIndex, origin, jsNumber(vectorLength), KnownInt32Use));
+                    data->m_properties.append(VectorLengthPLoc);
+
+                    m_graph.m_varArgChildren.append(
+                        m_insertionSet.insertConstantForUse(
+                            m_nodeIndex, origin, jsNumber(result.start), UntypedUse));
+                    data->m_properties.append(
+                        PromotedLocationDescriptor(NamedPropertyPLoc, indexIndex));
+
+                    m_graph.m_varArgChildren.append(Edge(stringNode, UntypedUse));
+                    data->m_properties.append(
+                        PromotedLocationDescriptor(NamedPropertyPLoc, inputIndex));
+
+                    auto materializeString = [&] (const String& string) -> Node* {
+                        if (string.isNull())
+                            return nullptr;
+                        if (string.isEmpty()) {
+                            return m_insertionSet.insertConstant(
+                                m_nodeIndex, origin, vm().smallStrings.emptyString());
+                        }
+                        LazyJSValue value = LazyJSValue::newString(m_graph, string);
+                        return m_insertionSet.insertNode(
+                            m_nodeIndex, SpecNone, LazyJSConstant, origin,
+                            OpInfo(m_graph.m_lazyJSValues.add(value)));
+                    };
+
+                    for (unsigned i = 0; i < resultArray.size(); ++i) {
+                        if (Node* node = materializeString(resultArray[i])) {
+                            m_graph.m_varArgChildren.append(Edge(node, UntypedUse));
+                            data->m_properties.append(
+                                PromotedLocationDescriptor(IndexedPropertyPLoc, i));
+                        }
+                    }
+            
+                    Node* resultNode = m_insertionSet.insertNode(
+                        m_nodeIndex, SpecArray, Node::VarArg, MaterializeNewObject, origin,
+                        OpInfo(structureSet), OpInfo(data), firstChild,
+                        m_graph.m_varArgChildren.size() - firstChild);
+                
+                    m_node->convertToIdentityOn(resultNode);
+                } else
+                    m_graph.convertToConstant(m_node, jsNull());
+            } else
+                m_graph.convertToConstant(m_node, jsBoolean(!!result));
+
+            // Whether it's Exec or Test, we need to tell the constructor and RegExpObject what's up.
+            // Because SetRegExpObjectLastIndex may exit and it clobbers exit state, we do that
+            // first.
+            
+            if (regExp->globalOrSticky()) {
+                m_insertionSet.insertNode(
+                    m_nodeIndex, SpecNone, SetRegExpObjectLastIndex, origin,
+                    Edge(regExpObjectNode, RegExpObjectUse),
+                    m_insertionSet.insertConstantForUse(
+                        m_nodeIndex, origin, jsNumber(result ? result.end : 0), UntypedUse));
+                
+                origin = origin.withInvalidExit();
+            }
+
+            if (result) {
+                unsigned firstChild = m_graph.m_varArgChildren.size();
+                m_graph.m_varArgChildren.append(
+                    m_insertionSet.insertConstantForUse(
+                        m_nodeIndex, origin, constructorFrozenValue, KnownCellUse));
+                m_graph.m_varArgChildren.append(
+                    m_insertionSet.insertConstantForUse(
+                        m_nodeIndex, origin, regExpFrozenValue, KnownCellUse));
+                m_graph.m_varArgChildren.append(Edge(stringNode, KnownCellUse));
+                m_graph.m_varArgChildren.append(
+                    m_insertionSet.insertConstantForUse(
+                        m_nodeIndex, origin, jsNumber(result.start), KnownInt32Use));
+                m_graph.m_varArgChildren.append(
+                    m_insertionSet.insertConstantForUse(
+                        m_nodeIndex, origin, jsNumber(result.end), KnownInt32Use));
+                m_insertionSet.insertNode(
+                    m_nodeIndex, SpecNone, Node::VarArg, RecordRegExpCachedResult, origin,
+                    OpInfo(), OpInfo(), firstChild, m_graph.m_varArgChildren.size() - firstChild);
+
+                origin = origin.withInvalidExit();
+            }
+            
+            m_node->origin = origin;
+            break;
+        }
+
+        case StringReplace: {
+            Node* stringNode = m_node->child1().node();
+            String string = stringNode->tryGetString(m_graph);
+            if (!string)
+                break;
+            
+            Node* regExpObjectNode = m_node->child2().node();
+            RegExp* regExp;
+            if (RegExpObject* regExpObject = regExpObjectNode->dynamicCastConstant<RegExpObject*>())
+                regExp = regExpObject->regExp();
+            else if (regExpObjectNode->op() == NewRegexp)
+                regExp = codeBlock()->regexp(regExpObjectNode->regexpIndex());
+            else {
+                if (verbose)
+                    dataLog("Giving up because the regexp is unknown.\n");
+                break;
+            }
+
+            String replace = m_node->child3()->tryGetString(m_graph);
+            if (!string)
+                break;
+
+            StringBuilder builder;
+
+            unsigned lastIndex = 0;
+            unsigned startPosition = 0;
+            bool ok = true;
+            do {
+                MatchResult result;
+                Vector<int, 32> ovector;
+                // Model which version of match() is called by the main thread.
+                if (replace.isEmpty() && regExp->global()) {
+                    if (!regExp->matchConcurrently(vm(), string, startPosition, result)) {
+                        ok = false;
+                        break;
+                    }
+                } else {
+                    int position;
+                    if (!regExp->matchConcurrently(vm(), string, startPosition, position, ovector)) {
+                        ok = false;
+                        break;
+                    }
+                    
+                    result.start = position;
+                    result.end = ovector[1];
+                }
+                
+                if (!result)
+                    break;
+
+                unsigned replLen = replace.length();
+                if (lastIndex < result.start || replLen) {
+                    builder.append(string, lastIndex, result.start - lastIndex);
+                    if (replLen)
+                        builder.append(substituteBackreferences(replace, string, ovector.data(), regExp));
+                }
+
+                lastIndex = result.end;
+                startPosition = lastIndex;
+
+                // special case of empty match
+                if (result.empty()) {
+                    startPosition++;
+                    if (startPosition > string.length())
+                        break;
+                }
+            } while (regExp->global());
+            if (!ok)
+                break;
+
+            // We are committed at this point.
+            m_changed = true;
+
+            NodeOrigin origin = m_node->origin;
+
+            if (regExp->global()) {
+                m_insertionSet.insertNode(
+                    m_nodeIndex, SpecNone, SetRegExpObjectLastIndex, origin,
+                    Edge(regExpObjectNode, RegExpObjectUse),
+                    m_insertionSet.insertConstantForUse(
+                        m_nodeIndex, origin, jsNumber(0), UntypedUse));
+                
+                origin = origin.withInvalidExit();
+            }
+
+            if (!lastIndex && builder.isEmpty())
+                m_node->convertToIdentityOn(stringNode);
+            else {
+                if (lastIndex < string.length())
+                    builder.append(string, lastIndex, string.length() - lastIndex);
+                
+                m_node->convertToLazyJSConstant(
+                    m_graph, LazyJSValue::newString(m_graph, builder.toString()));
+            }
+
+            m_node->origin = origin;
+            break;
+        }
+
         default:
             break;
         }
@@ -380,6 +741,11 @@ private:
             return;
         }
     }
+
+    void executeInsertionSet()
+    {
+        m_nodeIndex += m_insertionSet.execute(m_block);
+    }
     
     InsertionSet m_insertionSet;
     BasicBlock* m_block;
index 5a82c12..cf86c5d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -281,6 +281,18 @@ public:
                         VALIDATE((node), !variant.oldStructureForTransition()->dfgShouldWatch());
                     }
                     break;
+                case MaterializeNewObject:
+                    for (Structure* structure : node->structureSet()) {
+                        // This only supports structures that are JSFinalObject or JSArray.
+                        VALIDATE(
+                            (node),
+                            structure->classInfo() == JSFinalObject::info()
+                            || structure->classInfo() == JSArray::info());
+
+                        // We only support certain indexing shapes.
+                        VALIDATE((node), !hasAnyArrayStorage(structure->indexingType()));
+                    }
+                    break;
                 case DoubleConstant:
                 case Int52Constant:
                     VALIDATE((node), node->isNumberConstant());
@@ -505,13 +517,42 @@ private:
                 case GetMyArgumentByVal:
                 case PutHint:
                 case CheckStructureImmediate:
-                case MaterializeNewObject:
                 case MaterializeCreateActivation:
                 case PutStack:
                 case KillStack:
                 case GetStack:
                     VALIDATE((node), !"unexpected node type in CPS");
                     break;
+                case MaterializeNewObject: {
+                    // CPS only allows array lengths to be constant. This constraint only exists
+                    // because we don't have DFG support for anything more and we don't need any
+                    // other kind of support for now.
+                    ObjectMaterializationData& data = node->objectMaterializationData();
+                    for (unsigned i = data.m_properties.size(); i--;) {
+                        PromotedLocationDescriptor descriptor = data.m_properties[i];
+                        Edge edge = m_graph.varArgChild(node, 1 + i);
+                        switch (descriptor.kind()) {
+                        case PublicLengthPLoc:
+                        case VectorLengthPLoc:
+                            VALIDATE((node, edge), edge->isInt32Constant());
+                            break;
+                        default:
+                            break;
+                        }
+                    }
+
+                    // CPS only allows one structure.
+                    VALIDATE((node), node->structureSet().size() == 1);
+
+                    // CPS disallows int32 and double arrays. Those require weird type checks and
+                    // conversions. They are not needed in the DFG right now. We should add support
+                    // for these if the DFG ever needs it.
+                    for (Structure* structure : node->structureSet()) {
+                        VALIDATE((node), !hasInt32(structure->indexingType()));
+                        VALIDATE((node), !hasDouble(structure->indexingType()));
+                    }
+                    break;
+                }
                 case Phantom:
                     VALIDATE((node), m_graph.m_fixpointState != FixpointNotConverged);
                     break;
index 970ede8..3c4f071 100644 (file)
@@ -39,6 +39,7 @@
 #include "JSPropertyNameEnumerator.h"
 #include "JSScope.h"
 #include "JSCInlines.h"
+#include "RegExpConstructor.h"
 #include "RegExpObject.h"
 #include "ScopedArguments.h"
 #include "ScopedArgumentsTable.h"
index 6fcbb1b..8344477 100644 (file)
@@ -77,6 +77,11 @@ namespace JSC { namespace FTL {
     macro(JSSymbolTableObject_symbolTable, JSSymbolTableObject::offsetOfSymbolTable()) \
     macro(JSWrapperObject_internalValue, JSWrapperObject::internalValueOffset()) \
     macro(MarkedAllocator_freeListHead, MarkedAllocator::offsetOfFreeListHead()) \
+    macro(RegExpConstructor_cachedResult_lastRegExp, RegExpConstructor::offsetOfCachedResult() + RegExpCachedResult::offsetOfLastRegExp()) \
+    macro(RegExpConstructor_cachedResult_lastInput, RegExpConstructor::offsetOfCachedResult() + RegExpCachedResult::offsetOfLastInput()) \
+    macro(RegExpConstructor_cachedResult_result_start, RegExpConstructor::offsetOfCachedResult() + RegExpCachedResult::offsetOfResult() + OBJECT_OFFSETOF(MatchResult, start)) \
+    macro(RegExpConstructor_cachedResult_result_end, RegExpConstructor::offsetOfCachedResult() + RegExpCachedResult::offsetOfResult() + OBJECT_OFFSETOF(MatchResult, end)) \
+    macro(RegExpConstructor_cachedResult_reified, RegExpConstructor::offsetOfCachedResult() + RegExpCachedResult::offsetOfReified()) \
     macro(RegExpObject_lastIndex, RegExpObject::offsetOfLastIndex()) \
     macro(RegExpObject_lastIndexIsWritable, RegExpObject::offsetOfLastIndexIsWritable()) \
     macro(ScopedArguments_overrodeThings, ScopedArguments::offsetOfOverrodeThings()) \
index 2c83848..2d2f61e 100644 (file)
@@ -228,6 +228,7 @@ inline CapabilityLevel canCompile(Node* node)
     case StringReplace:
     case GetRegExpObjectLastIndex:
     case SetRegExpObjectLastIndex:
+    case RecordRegExpCachedResult:
     case SetFunctionName:
         // These are OK.
         break;
index 0b15a71..5a09b91 100644 (file)
@@ -941,6 +941,9 @@ private:
         case SetRegExpObjectLastIndex:
             compileSetRegExpObjectLastIndex();
             break;
+        case RecordRegExpCachedResult:
+            compileRecordRegExpCachedResult();
+            break;
 
         case PhantomLocal:
         case LoopHint:
@@ -3724,32 +3727,8 @@ private:
             
             m_out.store32(publicLength, butterfly, m_heaps.Butterfly_publicLength);
             m_out.store32(vectorLength, butterfly, m_heaps.Butterfly_vectorLength);
-            
-            if (hasDouble(m_node->indexingType())) {
-                LBasicBlock initLoop = m_out.newBlock();
-                LBasicBlock initDone = m_out.newBlock();
-                
-                ValueFromBlock originalIndex = m_out.anchor(vectorLength);
-                ValueFromBlock originalPointer = m_out.anchor(butterfly);
-                m_out.branch(
-                    m_out.notZero32(vectorLength), unsure(initLoop), unsure(initDone));
-                
-                LBasicBlock initLastNext = m_out.appendTo(initLoop, initDone);
-                LValue index = m_out.phi(m_out.int32, originalIndex);
-                LValue pointer = m_out.phi(m_out.intPtr, originalPointer);
-                
-                m_out.store64(
-                    m_out.constInt64(bitwise_cast<int64_t>(PNaN)),
-                    TypedPointer(m_heaps.indexedDoubleProperties.atAnyIndex(), pointer));
-                
-                LValue nextIndex = m_out.sub(index, m_out.int32One);
-                m_out.addIncomingToPhi(index, m_out.anchor(nextIndex));
-                m_out.addIncomingToPhi(pointer, m_out.anchor(m_out.add(pointer, m_out.intPtrEight)));
-                m_out.branch(
-                    m_out.notZero32(nextIndex), unsure(initLoop), unsure(initDone));
-                
-                m_out.appendTo(initDone, initLastNext);
-            }
+
+            initializeArrayElements(m_node->indexingType(), vectorLength, butterfly);
             
             ValueFromBlock fastResult = m_out.anchor(object);
             m_out.jump(continuation);
@@ -6294,11 +6273,21 @@ private:
         // Lower the values first, to avoid creating values inside a control flow diamond.
         
         Vector<LValue, 8> values;
-        for (unsigned i = 0; i < data.m_properties.size(); ++i)
-            values.append(lowJSValue(m_graph.varArgChild(m_node, 1 + i)));
+        for (unsigned i = 0; i < data.m_properties.size(); ++i) {
+            Edge edge = m_graph.varArgChild(m_node, 1 + i);
+            switch (data.m_properties[i].kind()) {
+            case PublicLengthPLoc:
+            case VectorLengthPLoc:
+                values.append(lowInt32(edge));
+                break;
+            default:
+                values.append(lowJSValue(edge));
+                break;
+            }
+        }
         
         const StructureSet& set = m_node->structureSet();
-        
+
         Vector<LBasicBlock, 1> blocks(set.size());
         for (unsigned i = set.size(); i--;)
             blocks[i] = m_out.newBlock();
@@ -6323,21 +6312,51 @@ private:
             LValue object;
             LValue butterfly;
             
-            if (structure->outOfLineCapacity()) {
+            if (structure->outOfLineCapacity() || hasIndexedProperties(structure->indexingType())) {
                 size_t allocationSize = JSFinalObject::allocationSize(structure->inlineCapacity());
                 MarkedAllocator* allocator = &vm().heap.allocatorForObjectWithoutDestructor(allocationSize);
+
+                bool hasIndexingHeader = hasIndexedProperties(structure->indexingType());
+                unsigned indexingHeaderSize = 0;
+                LValue indexingPayloadSizeInBytes = m_out.intPtrZero;
+                LValue vectorLength = m_out.int32Zero;
+                LValue publicLength = m_out.int32Zero;
+                if (hasIndexingHeader) {
+                    indexingHeaderSize = sizeof(IndexingHeader);
+                    for (unsigned i = data.m_properties.size(); i--;) {
+                        PromotedLocationDescriptor descriptor = data.m_properties[i];
+                        switch (descriptor.kind()) {
+                        case PublicLengthPLoc:
+                            publicLength = values[i];
+                            break;
+                        case VectorLengthPLoc:
+                            vectorLength = values[i];
+                            break;
+                        default:
+                            break;
+                        }
+                    }
+                    indexingPayloadSizeInBytes =
+                        m_out.mul(m_out.zeroExtPtr(vectorLength), m_out.intPtrEight);
+                }
+
+                LValue butterflySize = m_out.add(
+                    m_out.constIntPtr(
+                        structure->outOfLineCapacity() * sizeof(JSValue) + indexingHeaderSize),
+                    indexingPayloadSizeInBytes);
                 
                 LBasicBlock slowPath = m_out.newBlock();
                 LBasicBlock continuation = m_out.newBlock();
                 
                 LBasicBlock lastNext = m_out.insertNewBlocksBefore(slowPath);
                 
-                LValue endOfStorage = allocateBasicStorageAndGetEnd(
-                    m_out.constIntPtr(structure->outOfLineCapacity() * sizeof(JSValue)),
-                    slowPath);
-                
+                LValue endOfStorage = allocateBasicStorageAndGetEnd(butterflySize, slowPath);
+
                 LValue fastButterflyValue = m_out.add(
-                    m_out.constIntPtr(sizeof(IndexingHeader)), endOfStorage);
+                    m_out.sub(endOfStorage, indexingPayloadSizeInBytes),
+                    m_out.constIntPtr(sizeof(IndexingHeader) - indexingHeaderSize));
+
+                m_out.store32(vectorLength, fastButterflyValue, m_heaps.Butterfly_vectorLength);
                 
                 LValue fastObjectValue = allocateObject(
                     m_out.constIntPtr(allocator), structure, fastButterflyValue, slowPath);
@@ -6348,12 +6367,24 @@ private:
                 
                 m_out.appendTo(slowPath, continuation);
 
-                LValue slowObjectValue = lazySlowPath(
-                    [=] (const Vector<Location>& locations) -> RefPtr<LazySlowPath::Generator> {
-                        return createLazyCallGenerator(
-                            operationNewObjectWithButterfly, locations[0].directGPR(),
-                            CCallHelpers::TrustedImmPtr(structure));
-                    });
+                LValue slowObjectValue;
+                if (hasIndexingHeader) {
+                    slowObjectValue = lazySlowPath(
+                        [=] (const Vector<Location>& locations) -> RefPtr<LazySlowPath::Generator> {
+                            return createLazyCallGenerator(
+                                operationNewObjectWithButterflyWithIndexingHeaderAndVectorLength,
+                                locations[0].directGPR(), CCallHelpers::TrustedImmPtr(structure),
+                                locations[1].directGPR());
+                        },
+                        vectorLength);
+                } else {
+                    slowObjectValue = lazySlowPath(
+                        [=] (const Vector<Location>& locations) -> RefPtr<LazySlowPath::Generator> {
+                            return createLazyCallGenerator(
+                                operationNewObjectWithButterfly, locations[0].directGPR(),
+                                CCallHelpers::TrustedImmPtr(structure));
+                        });
+                }
                 ValueFromBlock slowObject = m_out.anchor(slowObjectValue);
                 ValueFromBlock slowButterfly = m_out.anchor(
                     m_out.loadPtr(slowObjectValue, m_heaps.JSObject_butterfly));
@@ -6364,6 +6395,111 @@ private:
                 
                 object = m_out.phi(m_out.intPtr, fastObject, slowObject);
                 butterfly = m_out.phi(m_out.intPtr, fastButterfly, slowButterfly);
+
+                m_out.store32(publicLength, butterfly, m_heaps.Butterfly_publicLength);
+
+                initializeArrayElements(structure->indexingType(), vectorLength, butterfly);
+
+                HashMap<int32_t, LValue, DefaultHash<int32_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<int32_t>> indexMap;
+                Vector<int32_t> indices;
+                for (unsigned i = data.m_properties.size(); i--;) {
+                    PromotedLocationDescriptor descriptor = data.m_properties[i];
+                    if (descriptor.kind() != IndexedPropertyPLoc)
+                        continue;
+                    int32_t index = static_cast<int32_t>(descriptor.info());
+                    
+                    auto result = indexMap.add(index, values[i]);
+                    DFG_ASSERT(m_graph, m_node, result); // Duplicates are illegal.
+
+                    indices.append(index);
+                }
+
+                if (!indices.isEmpty()) {
+                    std::sort(indices.begin(), indices.end());
+                    
+                    Vector<LBasicBlock> blocksWithStores(indices.size());
+                    Vector<LBasicBlock> blocksWithChecks(indices.size());
+                    
+                    for (unsigned i = indices.size(); i--;) {
+                        blocksWithStores[i] = m_out.newBlock();
+                        blocksWithChecks[i] = m_out.newBlock(); // blocksWithChecks[0] is the continuation.
+                    }
+
+                    LBasicBlock indexLastNext = m_out.m_nextBlock;
+                    
+                    for (unsigned i = indices.size(); i--;) {
+                        int32_t index = indices[i];
+                        LValue value = indexMap.get(index);
+                        
+                        m_out.branch(
+                            m_out.below(m_out.constInt32(index), publicLength),
+                            unsure(blocksWithStores[i]), unsure(blocksWithChecks[i]));
+
+                        m_out.appendTo(blocksWithStores[i], blocksWithChecks[i]);
+
+                        // This has to type-check and convert its inputs, but it cannot do so in a
+                        // way that updates AI. That's a bit annoying, but if you think about how
+                        // sinking works, it's actually not a bad thing. We are virtually guaranteed
+                        // that these type checks will not fail, since the type checks that guarded
+                        // the original stores to the array are still somewhere above this point.
+                        Output::StoreType storeType;
+                        IndexedAbstractHeap* heap;
+                        switch (structure->indexingType()) {
+                        case ALL_INT32_INDEXING_TYPES:
+                            // FIXME: This could use the proven type if we had the Edge for the
+                            // value. https://bugs.webkit.org/show_bug.cgi?id=155311
+                            speculate(BadType, noValue(), nullptr, isNotInt32(value));
+                            storeType = Output::Store64;
+                            heap = &m_heaps.indexedInt32Properties;
+                            break;
+
+                        case ALL_DOUBLE_INDEXING_TYPES: {
+                            // FIXME: If the source is ValueRep, we should avoid emitting any
+                            // checks. We could also avoid emitting checks if we had the Edge of
+                            // this value. https://bugs.webkit.org/show_bug.cgi?id=155311
+
+                            LBasicBlock intCase = m_out.newBlock();
+                            LBasicBlock doubleCase = m_out.newBlock();
+                            LBasicBlock continuation = m_out.newBlock();
+
+                            m_out.branch(isInt32(value), unsure(intCase), unsure(doubleCase));
+
+                            LBasicBlock lastNext = m_out.appendTo(intCase, doubleCase);
+
+                            ValueFromBlock intResult =
+                                m_out.anchor(m_out.intToDouble(unboxInt32(value)));
+                            m_out.jump(continuation);
+
+                            m_out.appendTo(doubleCase, continuation);
+
+                            speculate(BadType, noValue(), nullptr, isNumber(value));
+                            ValueFromBlock doubleResult = m_out.anchor(unboxDouble(value));
+                            m_out.jump(continuation);
+
+                            m_out.appendTo(continuation, lastNext);
+                            value = m_out.phi(Double, intResult, doubleResult);
+                            storeType = Output::StoreDouble;
+                            heap = &m_heaps.indexedDoubleProperties;
+                            break;
+                        }
+
+                        case ALL_CONTIGUOUS_INDEXING_TYPES:
+                            storeType = Output::Store64;
+                            heap = &m_heaps.indexedContiguousProperties;
+                            break;
+
+                        default:
+                            DFG_CRASH(m_graph, m_node, "Invalid indexing type");
+                            break;
+                        }
+                        
+                        m_out.store(value, m_out.address(butterfly, heap->at(index)), storeType);
+
+                        m_out.jump(blocksWithChecks[i]);
+                        m_out.appendTo(
+                            blocksWithChecks[i], i ? blocksWithStores[i - 1] : indexLastNext);
+                    }
+                }
             } else {
                 // In the easy case where we can do a one-shot allocation, we simply allocate the
                 // object to directly have the desired structure.
@@ -6373,12 +6509,14 @@ private:
             
             for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) {
                 for (unsigned i = data.m_properties.size(); i--;) {
-                    PhantomPropertyValue value = data.m_properties[i];
-                    if (m_graph.identifiers()[value.m_identifierNumber] != entry.key)
+                    PromotedLocationDescriptor descriptor = data.m_properties[i];
+                    if (descriptor.kind() != NamedPropertyPLoc)
+                        continue;
+                    if (m_graph.identifiers()[descriptor.info()] != entry.key)
                         continue;
                     
                     LValue base = isInlineOffset(entry.offset) ? object : butterfly;
-                    storeProperty(values[i], base, value.m_identifierNumber, entry.offset);
+                    storeProperty(values[i], base, descriptor.info(), entry.offset);
                     break;
                 }
             }
@@ -6443,9 +6581,11 @@ private:
         LValue activation = m_out.phi(m_out.intPtr, fastResult, slowResult);
         RELEASE_ASSERT(data.m_properties.size() == table->scopeSize());
         for (unsigned i = 0; i < data.m_properties.size(); ++i) {
-            m_out.store64(values[i],
-                activation,
-                m_heaps.JSEnvironmentRecord_variables[data.m_properties[i].m_identifierNumber]);
+            PromotedLocationDescriptor descriptor = data.m_properties[i];
+            ASSERT(descriptor.kind() == ClosureVarPLoc);
+            m_out.store64(
+                values[i], activation,
+                m_heaps.JSEnvironmentRecord_variables[descriptor.info()]);
         }
 
         if (validationEnabled()) {
@@ -6454,7 +6594,9 @@ private:
             for (auto iter = table->begin(locker), end = table->end(locker); iter != end; ++iter) {
                 bool found = false;
                 for (unsigned i = 0; i < data.m_properties.size(); ++i) {
-                    if (iter->value.scopeOffset().offset() == data.m_properties[i].m_identifierNumber) {
+                    PromotedLocationDescriptor descriptor = data.m_properties[i];
+                    ASSERT(descriptor.kind() == ClosureVarPLoc);
+                    if (iter->value.scopeOffset().offset() == descriptor.info()) {
                         found = true;
                         break;
                     }
@@ -6627,6 +6769,29 @@ private:
         m_out.store64(value, regExp, m_heaps.RegExpObject_lastIndex);
     }
 
+    void compileRecordRegExpCachedResult()
+    {
+        Edge constructorEdge = m_graph.varArgChild(m_node, 0);
+        Edge regExpEdge = m_graph.varArgChild(m_node, 1);
+        Edge stringEdge = m_graph.varArgChild(m_node, 2);
+        Edge startEdge = m_graph.varArgChild(m_node, 3);
+        Edge endEdge = m_graph.varArgChild(m_node, 4);
+        
+        LValue constructor = lowCell(constructorEdge);
+        LValue regExp = lowCell(regExpEdge);
+        LValue string = lowCell(stringEdge);
+        LValue start = lowInt32(startEdge);
+        LValue end = lowInt32(endEdge);
+
+        m_out.storePtr(regExp, constructor, m_heaps.RegExpConstructor_cachedResult_lastRegExp);
+        m_out.storePtr(string, constructor, m_heaps.RegExpConstructor_cachedResult_lastInput);
+        m_out.store32(start, constructor, m_heaps.RegExpConstructor_cachedResult_result_start);
+        m_out.store32(end, constructor, m_heaps.RegExpConstructor_cachedResult_result_end);
+        m_out.store32As8(
+            m_out.constInt32(0),
+            m_out.address(constructor, m_heaps.RegExpConstructor_cachedResult_reified));
+    }
+
     LValue didOverflowStack()
     {
         // This does a very simple leaf function analysis. The invariant of FTL call
@@ -6984,6 +7149,39 @@ private:
         
         return result;
     }
+
+    void initializeArrayElements(IndexingType indexingType, LValue vectorLength, LValue butterfly)
+    {
+        if (!hasDouble(indexingType)) {
+            // The GC already initialized everything to JSValue() for us.
+            return;
+        }
+
+        // Doubles must be initialized to PNaN.
+        LBasicBlock initLoop = m_out.newBlock();
+        LBasicBlock initDone = m_out.newBlock();
+        
+        ValueFromBlock originalIndex = m_out.anchor(vectorLength);
+        ValueFromBlock originalPointer = m_out.anchor(butterfly);
+        m_out.branch(
+            m_out.notZero32(vectorLength), unsure(initLoop), unsure(initDone));
+        
+        LBasicBlock initLastNext = m_out.appendTo(initLoop, initDone);
+        LValue index = m_out.phi(m_out.int32, originalIndex);
+        LValue pointer = m_out.phi(m_out.intPtr, originalPointer);
+        
+        m_out.store64(
+            m_out.constInt64(bitwise_cast<int64_t>(PNaN)),
+            TypedPointer(m_heaps.indexedDoubleProperties.atAnyIndex(), pointer));
+        
+        LValue nextIndex = m_out.sub(index, m_out.int32One);
+        m_out.addIncomingToPhi(index, m_out.anchor(nextIndex));
+        m_out.addIncomingToPhi(pointer, m_out.anchor(m_out.add(pointer, m_out.intPtrEight)));
+        m_out.branch(
+            m_out.notZero32(nextIndex), unsure(initLoop), unsure(initDone));
+        
+        m_out.appendTo(initDone, initLastNext);
+    }
     
     LValue allocatePropertyStorage(LValue object, Structure* previousStructure)
     {
@@ -9167,7 +9365,7 @@ private:
             return proven;
         return m_out.testNonZero64(jsValue, m_tagTypeNumber);
     }
-    
+
     LValue unboxDouble(LValue jsValue)
     {
         return m_out.bitCast(m_out.add(jsValue, m_tagTypeNumber), m_out.doubleType);
index 664fc87..beeebc0 100644 (file)
@@ -41,19 +41,6 @@ namespace JSC { namespace FTL {
 
 using namespace JSC::DFG;
 
-extern "C" JSCell* JIT_OPERATION operationNewObjectWithButterfly(ExecState* exec, Structure* structure)
-{
-    VM& vm = exec->vm();
-    NativeCallFrameTracer tracer(&vm, exec);
-    
-    Butterfly* butterfly = Butterfly::create(
-        vm, nullptr, 0, structure->outOfLineCapacity(), false, IndexingHeader(), 0);
-    
-    JSObject* result = JSFinalObject::create(exec, structure, butterfly);
-    result->butterfly(); // Ensure that the butterfly is in to-space.
-    return result;
-}
-
 extern "C" void JIT_OPERATION operationPopulateObjectInOSR(
     ExecState* exec, ExitTimeObjectMaterialization* materialization,
     EncodedJSValue* encodedValue, EncodedJSValue* values)
index 4f38c24..41b9b33 100644 (file)
@@ -37,8 +37,6 @@ class LazySlowPath;
 
 extern "C" {
 
-JSCell* JIT_OPERATION operationNewObjectWithButterfly(ExecState*, Structure*) WTF_INTERNAL;
-
 JSCell* JIT_OPERATION operationMaterializeObjectInOSR(
     ExecState*, ExitTimeObjectMaterialization*, EncodedJSValue*) WTF_INTERNAL;
 
index aef7812..6b64014 100644 (file)
@@ -32,6 +32,7 @@
 #include "InspectorValues.h"
 #include "RegularExpression.h"
 #include "Yarr.h"
+#include "YarrInterpreter.h"
 #include <wtf/BumpPointerAllocator.h>
 #include <wtf/StdLibExtras.h>
 #include <wtf/text/StringBuilder.h>
index ee0533d..99df16a 100644 (file)
@@ -97,6 +97,10 @@ class JSObject : public JSCell {
 public:
     typedef JSCell Base;
 
+    // This is a super dangerous method for JITs. Sometimes the JITs will want to create either a
+    // JSFinalObject or a JSArray. This is the method that will do that.
+    static JSObject* createRawObject(ExecState* exec, Structure* structure, Butterfly* = nullptr);
+
     JS_EXPORT_PRIVATE static size_t estimatedSize(JSCell*);
     JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
     JS_EXPORT_PRIVATE static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken);
@@ -1041,6 +1045,20 @@ private:
 
 JS_EXPORT_PRIVATE EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState*);
 
+inline JSObject* JSObject::createRawObject(
+    ExecState* exec, Structure* structure, Butterfly* butterfly)
+{
+    JSObject* finalObject = new (
+        NotNull, 
+        allocateCell<JSFinalObject>(
+            *exec->heap(),
+            JSFinalObject::allocationSize(structure->inlineCapacity())
+        )
+    ) JSObject(exec->vm(), structure, butterfly);
+    finalObject->finishCreation(exec->vm());
+    return finalObject;
+}
+
 inline JSFinalObject* JSFinalObject::create(
     ExecState* exec, Structure* structure, Butterfly* butterfly)
 {
index c31cf52..b9bb8f3 100644 (file)
@@ -262,6 +262,8 @@ RegExp* RegExp::create(VM& vm, const String& patternString, RegExpFlags flags)
 
 void RegExp::compile(VM* vm, Yarr::YarrCharSize charSize)
 {
+    ConcurrentJITLocker locker(m_lock);
+    
     Yarr::YarrPattern pattern(m_patternString, m_flags, &m_constructionError);
     if (m_constructionError) {
         RELEASE_ASSERT_NOT_REACHED();
@@ -291,7 +293,7 @@ void RegExp::compile(VM* vm, Yarr::YarrCharSize charSize)
 #endif
 
     m_state = ByteCode;
-    m_regExpBytecode = Yarr::byteCompile(pattern, &vm->m_regExpAllocator);
+    m_regExpBytecode = Yarr::byteCompile(pattern, &vm->m_regExpAllocator, &vm->m_regExpAllocatorLock);
 }
 
 int RegExp::match(VM& vm, const String& s, unsigned startOffset, Vector<int, 32>& ovector)
@@ -299,8 +301,22 @@ int RegExp::match(VM& vm, const String& s, unsigned startOffset, Vector<int, 32>
     return matchInline(vm, s, startOffset, ovector);
 }
 
+bool RegExp::matchConcurrently(
+    VM& vm, const String& s, unsigned startOffset, int& position, Vector<int, 32>& ovector)
+{
+    ConcurrentJITLocker locker(m_lock);
+
+    if (!hasCodeFor(s.is8Bit() ? Yarr::Char8 : Yarr::Char16))
+        return false;
+
+    position = match(vm, s, startOffset, ovector);
+    return true;
+}
+
 void RegExp::compileMatchOnly(VM* vm, Yarr::YarrCharSize charSize)
 {
+    ConcurrentJITLocker locker(m_lock);
+    
     Yarr::YarrPattern pattern(m_patternString, m_flags, &m_constructionError);
     if (m_constructionError) {
         RELEASE_ASSERT_NOT_REACHED();
@@ -330,7 +346,7 @@ void RegExp::compileMatchOnly(VM* vm, Yarr::YarrCharSize charSize)
 #endif
 
     m_state = ByteCode;
-    m_regExpBytecode = Yarr::byteCompile(pattern, &vm->m_regExpAllocator);
+    m_regExpBytecode = Yarr::byteCompile(pattern, &vm->m_regExpAllocator, &vm->m_regExpAllocatorLock);
 }
 
 MatchResult RegExp::match(VM& vm, const String& s, unsigned startOffset)
@@ -338,8 +354,21 @@ MatchResult RegExp::match(VM& vm, const String& s, unsigned startOffset)
     return matchInline(vm, s, startOffset);
 }
 
+bool RegExp::matchConcurrently(VM& vm, const String& s, unsigned startOffset, MatchResult& result)
+{
+    ConcurrentJITLocker locker(m_lock);
+
+    if (!hasMatchOnlyCodeFor(s.is8Bit() ? Yarr::Char8 : Yarr::Char16))
+        return false;
+
+    result = match(vm, s, startOffset);
+    return true;
+}
+
 void RegExp::deleteCode()
 {
+    ConcurrentJITLocker locker(m_lock);
+    
     if (!hasCode())
         return;
     m_state = NotCompiled;
index ad451f9..e075785 100644 (file)
@@ -22,6 +22,7 @@
 #ifndef RegExp_h
 #define RegExp_h
 
+#include "ConcurrentJITLock.h"
 #include "ExecutableAllocator.h"
 #include "MatchResult.h"
 #include "RegExpKey.h"
@@ -56,6 +57,7 @@ public:
     bool ignoreCase() const { return m_flags & FlagIgnoreCase; }
     bool multiline() const { return m_flags & FlagMultiline; }
     bool sticky() const { return m_flags & FlagSticky; }
+    bool globalOrSticky() const { return global() || sticky(); }
     bool unicode() const { return m_flags & FlagUnicode; }
 
     const String& pattern() const { return m_patternString; }
@@ -64,8 +66,14 @@ public:
     const char* errorMessage() const { return m_constructionError; }
 
     JS_EXPORT_PRIVATE int match(VM&, const String&, unsigned startOffset, Vector<int, 32>& ovector);
+
+    // Returns false if we couldn't run the regular expression for any reason.
+    bool matchConcurrently(VM&, const String&, unsigned startOffset, int& position, Vector<int, 32>& ovector);
+    
     JS_EXPORT_PRIVATE MatchResult match(VM&, const String&, unsigned startOffset);
 
+    bool matchConcurrently(VM&, const String&, unsigned startOffset, MatchResult&);
+
     // Call these versions of the match functions if you're desperate for performance.
     int matchInline(VM&, const String&, unsigned startOffset, Vector<int, 32>& ovector);
     MatchResult matchInline(VM&, const String&, unsigned startOffset);
@@ -77,6 +85,9 @@ public:
         return m_state != NotCompiled;
     }
 
+    bool hasCodeFor(Yarr::YarrCharSize);
+    bool hasMatchOnlyCodeFor(Yarr::YarrCharSize);
+
     void deleteCode();
 
 #if ENABLE(REGEXP_TRACING)
@@ -132,6 +143,7 @@ private:
     unsigned m_rtMatchCallCount;
     unsigned m_rtMatchFoundCount;
 #endif
+    ConcurrentJITLock m_lock;
 
 #if ENABLE(YARR_JIT)
     Yarr::YarrCodeBlock m_regExpJITCode;
index 67831ae..1974f51 100644 (file)
@@ -75,6 +75,11 @@ public:
 
     void visitChildren(SlotVisitor&);
 
+    static ptrdiff_t offsetOfLastRegExp() { return OBJECT_OFFSETOF(RegExpCachedResult, m_lastRegExp); }
+    static ptrdiff_t offsetOfLastInput() { return OBJECT_OFFSETOF(RegExpCachedResult, m_lastInput); }
+    static ptrdiff_t offsetOfResult() { return OBJECT_OFFSETOF(RegExpCachedResult, m_result); }
+    static ptrdiff_t offsetOfReified() { return OBJECT_OFFSETOF(RegExpCachedResult, m_reified); }
+
 private:
     MatchResult m_result;
     bool m_reified;
index 2e0a1d6..203a5d1 100644 (file)
@@ -69,6 +69,8 @@ public:
 
     static void visitChildren(JSCell*, SlotVisitor&);
 
+    static ptrdiff_t offsetOfCachedResult() { return OBJECT_OFFSETOF(RegExpConstructor, m_cachedResult); }
+
 protected:
     void finishCreation(VM&, RegExpPrototype*, GetterSetter* species);
 
index fd36b4b..e80b9a9 100644 (file)
@@ -26,6 +26,7 @@
 #include "RegExp.h"
 #include "JSCInlines.h"
 #include "Yarr.h"
+#include "YarrInterpreter.h"
 #include "YarrJIT.h"
 
 #define REGEXP_FUNC_TEST_DATA_GEN 0
@@ -67,20 +68,27 @@ private:
 };
 #endif // REGEXP_FUNC_TEST_DATA_GEN
 
-ALWAYS_INLINE void RegExp::compileIfNecessary(VM& vm, Yarr::YarrCharSize charSize)
+ALWAYS_INLINE bool RegExp::hasCodeFor(Yarr::YarrCharSize charSize)
 {
     if (hasCode()) {
 #if ENABLE(YARR_JIT)
         if (m_state != JITCode)
-            return;
+            return true;
         if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCode()))
-            return;
+            return true;
         if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCode()))
-            return;
+            return true;
 #else
-        return;
+        return true;
 #endif
     }
+    return false;
+}
+
+ALWAYS_INLINE void RegExp::compileIfNecessary(VM& vm, Yarr::YarrCharSize charSize)
+{
+    if (hasCodeFor(charSize))
+        return;
 
     compile(&vm, charSize);
 }
@@ -152,21 +160,29 @@ ALWAYS_INLINE int RegExp::matchInline(VM& vm, const String& s, unsigned startOff
     return result;
 }
 
-ALWAYS_INLINE void RegExp::compileIfNecessaryMatchOnly(VM& vm, Yarr::YarrCharSize charSize)
+ALWAYS_INLINE bool RegExp::hasMatchOnlyCodeFor(Yarr::YarrCharSize charSize)
 {
     if (hasCode()) {
 #if ENABLE(YARR_JIT)
         if (m_state != JITCode)
-            return;
+            return true;
         if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCodeMatchOnly()))
-            return;
+            return true;
         if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCodeMatchOnly()))
-            return;
+            return true;
 #else
-        return;
+        return true;
 #endif
     }
 
+    return false;
+}
+
+ALWAYS_INLINE void RegExp::compileIfNecessaryMatchOnly(VM& vm, Yarr::YarrCharSize charSize)
+{
+    if (hasMatchOnlyCodeFor(charSize))
+        return;
+
     compileMatchOnly(&vm, charSize);
 }
 
index 5aa8193..83b254c 100644 (file)
@@ -63,7 +63,7 @@ JSValue RegExpObject::execInline(ExecState* exec, JSGlobalObject* globalObject,
     String input = string->value(exec); // FIXME: Handle errors. https://bugs.webkit.org/show_bug.cgi?id=155145
     VM& vm = globalObject->vm();
 
-    bool globalOrSticky = regExp->global() || regExp->sticky();
+    bool globalOrSticky = regExp->globalOrSticky();
 
     unsigned lastIndex;
     if (globalOrSticky) {
index 6e1bfc5..634d628 100644 (file)
@@ -282,7 +282,7 @@ static NEVER_INLINE String substituteBackreferencesSlow(StringView replacement,
     return substitutedReplacement.toString();
 }
 
-static inline String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg)
+inline String substituteBackreferencesInline(const String& replacement, StringView source, const int* ovector, RegExp* reg)
 {
     size_t i = replacement.find('$');
     if (UNLIKELY(i != notFound))
@@ -291,6 +291,11 @@ static inline String substituteBackreferences(const String& replacement, StringV
     return replacement;
 }
 
+String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg)
+{
+    return substituteBackreferencesInline(replacement, source, ovector, reg);
+}
+
 struct StringRange {
     StringRange(int pos, int len)
         : position(pos)
index a428ebf..ce64284 100644 (file)
@@ -27,6 +27,7 @@
 namespace JSC {
 
 class ObjectPrototype;
+class RegExp;
 class RegExpObject;
 
 class StringPrototype : public StringObject {
@@ -62,6 +63,8 @@ EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpEmptyStr(
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
     ExecState*, JSString* thisValue, RegExpObject* searchValue, JSString* replaceValue);
 
+String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg);
+
 EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeatCharacter(ExecState*);
 
 } // namespace JSC
index 000ec81..e9abb71 100644 (file)
@@ -29,6 +29,7 @@
 #ifndef VM_h
 #define VM_h
 
+#include "ConcurrentJITLock.h"
 #include "ControlFlowProfiler.h"
 #include "DateInstanceCache.h"
 #include "ExecutableAllocator.h"
@@ -541,6 +542,7 @@ public:
     RefPtr<TypedArrayController> m_typedArrayController;
     RegExpCache* m_regExpCache;
     BumpPointerAllocator m_regExpAllocator;
+    ConcurrentJITLock m_regExpAllocatorLock;
 
 #if ENABLE(REGEXP_TRACING)
     typedef ListHashSet<RegExp*> RTTraceList;
diff --git a/Source/JavaScriptCore/tests/stress/simple-regexp-exec-folding-fail.js b/Source/JavaScriptCore/tests/stress/simple-regexp-exec-folding-fail.js
new file mode 100644 (file)
index 0000000..404f27b
--- /dev/null
@@ -0,0 +1,11 @@
+function foo() {
+    return /(f)(o)(o)/.exec("bar");
+}
+
+noInline(foo);
+
+for (var i = 0; i < 10000; ++i) {
+    var result = foo();
+    if (result !== null)
+        throw "Error: bad result: " + result;
+}
diff --git a/Source/JavaScriptCore/tests/stress/simple-regexp-exec-folding.js b/Source/JavaScriptCore/tests/stress/simple-regexp-exec-folding.js
new file mode 100644 (file)
index 0000000..9039320
--- /dev/null
@@ -0,0 +1,19 @@
+function foo() {
+    return /(f)(o)(o)/.exec("foo");
+}
+
+noInline(foo);
+
+for (var i = 0; i < 10000; ++i) {
+    var result = foo();
+    if (result.length != 4)
+        throw "Error: bad result: " + result;
+    if (result[0] != "foo")
+        throw "Error: bad result: " + result;
+    if (result[1] != "f")
+        throw "Error: bad result: " + result;
+    if (result[2] != "o")
+        throw "Error: bad result: " + result;
+    if (result[3] != "o")
+        throw "Error: bad result: " + result;
+}
diff --git a/Source/JavaScriptCore/tests/stress/simple-regexp-test-folding-fail.js b/Source/JavaScriptCore/tests/stress/simple-regexp-test-folding-fail.js
new file mode 100644 (file)
index 0000000..b7ec383
--- /dev/null
@@ -0,0 +1,11 @@
+function foo() {
+    return /(f)(o)(o)/.test("bar");
+}
+
+noInline(foo);
+
+for (var i = 0; i < 10000; ++i) {
+    var result = foo();
+    if (result != false)
+        throw "Error: bad result: " + result;
+}
diff --git a/Source/JavaScriptCore/tests/stress/simple-regexp-test-folding.js b/Source/JavaScriptCore/tests/stress/simple-regexp-test-folding.js
new file mode 100644 (file)
index 0000000..ef9b840
--- /dev/null
@@ -0,0 +1,11 @@
+function foo() {
+    return /(f)(o)(o)/.test("foo");
+}
+
+noInline(foo);
+
+for (var i = 0; i < 10000; ++i) {
+    var result = foo();
+    if (result != true)
+        throw "Error: bad result: " + result;
+}
index 1c435b6..d87d603 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2004, 2008, 2009 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Collabora Ltd.
@@ -30,6 +29,7 @@
 #include "RegularExpression.h"
 
 #include "Yarr.h"
+#include "YarrInterpreter.h"
 #include <wtf/Assertions.h>
 #include <wtf/BumpPointerAllocator.h>
 
index cfcf3ea..e49445f 100644 (file)
@@ -28,7 +28,6 @@
 #ifndef Yarr_h
 #define Yarr_h
 
-#include "YarrInterpreter.h"
 #include "YarrPattern.h"
 
 namespace JSC { namespace Yarr {
@@ -63,6 +62,8 @@ enum YarrCharSize {
     Char16
 };
 
+struct BytecodePattern;
+
 } } // namespace JSC::Yarr
 
 #endif // Yarr_h
index aeca421..75ebb80 100644 (file)
@@ -1522,6 +1522,9 @@ public:
         if (!input.isAvailableInput(0))
             return offsetNoMatch;
 
+        if (pattern->m_lock)
+            pattern->m_lock->lock();
+        
         for (unsigned i = 0; i < pattern->m_body->m_numSubpatterns + 1; ++i)
             output[i << 1] = offsetNoMatch;
 
@@ -1541,6 +1544,10 @@ public:
         pattern->m_allocator->stopAllocator();
 
         ASSERT((result == JSRegExpMatch) == (output[0] != offsetNoMatch));
+
+        if (pattern->m_lock)
+            pattern->m_lock->unlock();
+        
         return output[0];
     }
 
@@ -1581,13 +1588,13 @@ public:
         m_currentAlternativeIndex = 0;
     }
 
-    std::unique_ptr<BytecodePattern> compile(BumpPointerAllocator* allocator)
+    std::unique_ptr<BytecodePattern> compile(BumpPointerAllocator* allocator, ConcurrentJITLock* lock)
     {
         regexBegin(m_pattern.m_numSubpatterns, m_pattern.m_body->m_callFrameSize, m_pattern.m_body->m_alternatives[0]->onceThrough());
         emitDisjunction(m_pattern.m_body);
         regexEnd();
 
-        return std::make_unique<BytecodePattern>(WTFMove(m_bodyDisjunction), m_allParenthesesInfo, m_pattern, allocator);
+        return std::make_unique<BytecodePattern>(WTFMove(m_bodyDisjunction), m_allParenthesesInfo, m_pattern, allocator, lock);
     }
 
     void checkInput(unsigned count)
@@ -2032,9 +2039,9 @@ private:
     Vector<std::unique_ptr<ByteDisjunction>> m_allParenthesesInfo;
 };
 
-std::unique_ptr<BytecodePattern> byteCompile(YarrPattern& pattern, BumpPointerAllocator* allocator)
+std::unique_ptr<BytecodePattern> byteCompile(YarrPattern& pattern, BumpPointerAllocator* allocator, ConcurrentJITLock* lock)
 {
-    return ByteCompiler(pattern).compile(allocator);
+    return ByteCompiler(pattern).compile(allocator, lock);
 }
 
 unsigned interpret(BytecodePattern* bytecode, const String& input, unsigned start, unsigned* output)
index 176fc6d..011271d 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef YarrInterpreter_h
 #define YarrInterpreter_h
 
+#include "ConcurrentJITLock.h"
 #include "YarrPattern.h"
 
 namespace WTF {
@@ -337,10 +338,11 @@ public:
 struct BytecodePattern {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    BytecodePattern(std::unique_ptr<ByteDisjunction> body, Vector<std::unique_ptr<ByteDisjunction>>& parenthesesInfoToAdopt, YarrPattern& pattern, BumpPointerAllocator* allocator)
+    BytecodePattern(std::unique_ptr<ByteDisjunction> body, Vector<std::unique_ptr<ByteDisjunction>>& parenthesesInfoToAdopt, YarrPattern& pattern, BumpPointerAllocator* allocator, ConcurrentJITLock* lock)
         : m_body(WTFMove(body))
         , m_flags(pattern.m_flags)
         , m_allocator(allocator)
+        , m_lock(lock)
     {
         m_body->terms.shrinkToFit();
 
@@ -366,6 +368,7 @@ public:
     // Each BytecodePattern is associated with a RegExp, each RegExp is associated
     // with a VM.  Cache a pointer to out VM's m_regExpAllocator.
     BumpPointerAllocator* m_allocator;
+    ConcurrentJITLock* m_lock;
 
     CharacterClass* newlineCharacterClass;
     CharacterClass* wordcharCharacterClass;
@@ -375,7 +378,7 @@ private:
     Vector<std::unique_ptr<CharacterClass>> m_userCharacterClasses;
 };
 
-JS_EXPORT_PRIVATE std::unique_ptr<BytecodePattern> byteCompile(YarrPattern&, BumpPointerAllocator*);
+JS_EXPORT_PRIVATE std::unique_ptr<BytecodePattern> byteCompile(YarrPattern&, BumpPointerAllocator*, ConcurrentJITLock* = nullptr);
 JS_EXPORT_PRIVATE unsigned interpret(BytecodePattern*, const String& input, unsigned start, unsigned* output);
 unsigned interpret(BytecodePattern*, const LChar* input, unsigned length, unsigned start, unsigned* output);
 unsigned interpret(BytecodePattern*, const UChar* input, unsigned length, unsigned start, unsigned* output);
index c773ce8..d07ade2 100644 (file)
@@ -1,3 +1,16 @@
+2016-03-19  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG and FTL should constant-fold RegExpExec
+        https://bugs.webkit.org/show_bug.cgi?id=155270
+
+        Reviewed by Saam Barati.
+
+        Make executeInsertions() return the amount by which the vector increased in size. This is a
+        convenient feature that I use in DFG::InsertionSet.
+
+        * wtf/Insertion.h:
+        (WTF::executeInsertions):
+
 2016-04-05  Antoine Quint  <graouts@apple.com>
 
         [WebGL2] Allow enabling WebGL2 with a runtime flag
index a473d7e..c683580 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -55,14 +55,15 @@ private:
 };
 
 template<typename TargetVectorType, typename InsertionVectorType>
-void executeInsertions(TargetVectorType& target, InsertionVectorType& insertions)
+size_t executeInsertions(TargetVectorType& target, InsertionVectorType& insertions)
 {
-    if (!insertions.size())
-        return;
-    target.grow(target.size() + insertions.size());
+    size_t numInsertions = insertions.size();
+    if (!numInsertions)
+        return 0;
+    target.grow(target.size() + numInsertions);
     size_t lastIndex = target.size();
     size_t originalTargetSize = target.size();
-    for (size_t indexInInsertions = insertions.size(); indexInInsertions--;) {
+    for (size_t indexInInsertions = numInsertions; indexInInsertions--;) {
         ASSERT(!indexInInsertions || insertions[indexInInsertions].index() >= insertions[indexInInsertions - 1].index());
         ASSERT_UNUSED(originalTargetSize, insertions[indexInInsertions].index() < originalTargetSize);
         size_t firstIndex = insertions[indexInInsertions].index() + indexInInsertions;
@@ -73,6 +74,7 @@ void executeInsertions(TargetVectorType& target, InsertionVectorType& insertions
         lastIndex = firstIndex;
     }
     insertions.resize(0);
+    return numInsertions;
 }
 
 } // namespace WTF