[FTL] Drop NewRegexp for String.prototype.match with RegExp + global flag
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 11 Mar 2018 07:20:29 +0000 (07:20 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 11 Mar 2018 07:20:29 +0000 (07:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=181848

Reviewed by Sam Weinig.

JSTests:

* microbenchmarks/regexp-u-global-es5.js: Added.
(fn):
* microbenchmarks/regexp-u-global-es6.js: Added.
(fn):
* stress/materialized-regexp-has-correct-last-index-set-by-match-at-osr-exit.js: Added.
(shouldBe):
(test):
(i.switch):
* stress/materialized-regexp-has-correct-last-index-set-by-match.js: Added.
(shouldBe):
(test):

Source/JavaScriptCore:

In r181535, we support `string.match(/nonglobal/)` code. However, `string.match(/global/g)` is not
optimized since it sets `lastIndex` value before performing RegExp operation.

This patch optimizes the above "with a global flag" case by emitting `SetRegExpObjectLastIndex` properly.
RegExpMatchFast is converted to SetRegExpObjectLastIndex and RegExpMatchFastGlobal. The latter node
just holds RegExp (not RegExpObject) cell so that it can offer a chance to make NewRegexp PhantomNewRegexp
in object allocation sinking phase.

Added microbenchmarks shows that this patch makes NewRegexp PhantomNewRegexp even if the given RegExp
has a global flag. And it improves the performance.

                              baseline                  patched

regexp-u-global-es5       44.1298+-4.6128     ^     33.7920+-2.0110        ^ definitely 1.3059x faster
regexp-u-global-es6      182.3272+-2.2861     ^    154.3414+-7.6769        ^ definitely 1.1813x faster

* 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/DFGMayExit.cpp:
* dfg/DFGNode.cpp:
(JSC::DFG::Node::convertToRegExpMatchFastGlobal):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::hasCellOperand):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileRegExpMatchFastGlobal):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::handleNode):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileRegExpMatchFastGlobal):
* runtime/RegExpObject.cpp:
(JSC::collectMatches): Deleted.
* runtime/RegExpObject.h:
* runtime/RegExpObjectInlines.h:
(JSC::RegExpObject::execInline):
(JSC::RegExpObject::matchInline):
(JSC::advanceStringUnicode):
(JSC::collectMatches):
(JSC::RegExpObject::advanceStringUnicode): Deleted.
* runtime/RegExpPrototype.cpp:
(JSC::advanceStringIndex):

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

29 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/regexp-u-global-es5.js [new file with mode: 0644]
JSTests/microbenchmarks/regexp-u-global-es6.js [new file with mode: 0644]
JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match-at-osr-exit.js [new file with mode: 0644]
JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGMayExit.cpp
Source/JavaScriptCore/dfg/DFGNode.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
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/DFGStrengthReductionPhase.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/RegExpObject.cpp
Source/JavaScriptCore/runtime/RegExpObject.h
Source/JavaScriptCore/runtime/RegExpObjectInlines.h
Source/JavaScriptCore/runtime/RegExpPrototype.cpp

index 3e94c61..9843b3c 100644 (file)
@@ -1,3 +1,22 @@
+2018-03-10  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [FTL] Drop NewRegexp for String.prototype.match with RegExp + global flag
+        https://bugs.webkit.org/show_bug.cgi?id=181848
+
+        Reviewed by Sam Weinig.
+
+        * microbenchmarks/regexp-u-global-es5.js: Added.
+        (fn):
+        * microbenchmarks/regexp-u-global-es6.js: Added.
+        (fn):
+        * stress/materialized-regexp-has-correct-last-index-set-by-match-at-osr-exit.js: Added.
+        (shouldBe):
+        (test):
+        (i.switch):
+        * stress/materialized-regexp-has-correct-last-index-set-by-match.js: Added.
+        (shouldBe):
+        (test):
+
 2018-03-07  Dominik Infuehr  <dinfuehr@igalia.com>
 
         Disable test stress/var-injection-cache-invalidation.js on systems with limited memory
diff --git a/JSTests/microbenchmarks/regexp-u-global-es5.js b/JSTests/microbenchmarks/regexp-u-global-es5.js
new file mode 100644 (file)
index 0000000..32d32e6
--- /dev/null
@@ -0,0 +1,7 @@
+function fn() {
+    return '𠮷'.match(/^.$/g);
+}
+noInline(fn);
+
+for (var i = 0; i < 1e6; ++i)
+    fn();
diff --git a/JSTests/microbenchmarks/regexp-u-global-es6.js b/JSTests/microbenchmarks/regexp-u-global-es6.js
new file mode 100644 (file)
index 0000000..c4daef6
--- /dev/null
@@ -0,0 +1,7 @@
+function fn() {
+    return '𠮷'.match(/^.$/ug);
+}
+noInline(fn);
+
+for (var i = 0; i < 1e6; ++i)
+    fn();
diff --git a/JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match-at-osr-exit.js b/JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match-at-osr-exit.js
new file mode 100644 (file)
index 0000000..ea77505
--- /dev/null
@@ -0,0 +1,41 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(num, string)
+{
+    var regexp = /hello/g;
+    regexp.lastIndex = "Cocoa";
+    if (num === 2)
+        return regexp.lastIndex;
+    var result = string.match(regexp);
+    if (num === 1) {
+        OSRExit();
+        return [result, regexp];
+    }
+    return regexp.lastIndex;
+}
+noInline(test);
+
+for (var i = 0; i < 1e5; ++i) {
+    var num = i % 3;
+    switch (num) {
+    case 0:
+        shouldBe(test(num, "hellohello"), 0);
+        break;
+    case 1:
+        break;
+    case 2:
+        shouldBe(test(num, "hellohello"), "Cocoa");
+        break;
+    }
+}
+
+var [result, regexp] = test(1, "hellohello");
+shouldBe(regexp instanceof RegExp, true);
+shouldBe(regexp.lastIndex, 0);
+shouldBe(result.length, 2);
+shouldBe(result[0], "hello");
+shouldBe(result[1], "hello");
diff --git a/JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match.js b/JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match.js
new file mode 100644 (file)
index 0000000..fb55123
--- /dev/null
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(flag, string)
+{
+    var regexp = /hello/g;
+    regexp.lastIndex = "Cocoa";
+    var result = string.match(regexp);
+    if (flag)
+        return [result, regexp];
+    return regexp.lastIndex;
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i) {
+    if (i & 0x1) {
+        var [result, regexp] = test(true, "hellohello");
+        shouldBe(regexp instanceof RegExp, true);
+        shouldBe(regexp.lastIndex, 0);
+        shouldBe(result.length, 2);
+        shouldBe(result[0], "hello");
+        shouldBe(result[1], "hello");
+    } else
+        shouldBe(test(false, "hellohello"), 0);
+}
index 1962a01..3188f1b 100644 (file)
@@ -1,5 +1,74 @@
 2018-03-10  Yusuke Suzuki  <utatane.tea@gmail.com>
 
+        [FTL] Drop NewRegexp for String.prototype.match with RegExp + global flag
+        https://bugs.webkit.org/show_bug.cgi?id=181848
+
+        Reviewed by Sam Weinig.
+
+        In r181535, we support `string.match(/nonglobal/)` code. However, `string.match(/global/g)` is not
+        optimized since it sets `lastIndex` value before performing RegExp operation.
+
+        This patch optimizes the above "with a global flag" case by emitting `SetRegExpObjectLastIndex` properly.
+        RegExpMatchFast is converted to SetRegExpObjectLastIndex and RegExpMatchFastGlobal. The latter node
+        just holds RegExp (not RegExpObject) cell so that it can offer a chance to make NewRegexp PhantomNewRegexp
+        in object allocation sinking phase.
+
+        Added microbenchmarks shows that this patch makes NewRegexp PhantomNewRegexp even if the given RegExp
+        has a global flag. And it improves the performance.
+
+                                      baseline                  patched
+
+        regexp-u-global-es5       44.1298+-4.6128     ^     33.7920+-2.0110        ^ definitely 1.3059x faster
+        regexp-u-global-es6      182.3272+-2.2861     ^    154.3414+-7.6769        ^ definitely 1.1813x faster
+
+        * 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/DFGMayExit.cpp:
+        * dfg/DFGNode.cpp:
+        (JSC::DFG::Node::convertToRegExpMatchFastGlobal):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        (JSC::DFG::Node::hasCellOperand):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileRegExpMatchFastGlobal):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStrengthReductionPhase.cpp:
+        (JSC::DFG::StrengthReductionPhase::handleNode):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileRegExpMatchFastGlobal):
+        * runtime/RegExpObject.cpp:
+        (JSC::collectMatches): Deleted.
+        * runtime/RegExpObject.h:
+        * runtime/RegExpObjectInlines.h:
+        (JSC::RegExpObject::execInline):
+        (JSC::RegExpObject::matchInline):
+        (JSC::advanceStringUnicode):
+        (JSC::collectMatches):
+        (JSC::RegExpObject::advanceStringUnicode): Deleted.
+        * runtime/RegExpPrototype.cpp:
+        (JSC::advanceStringIndex):
+
+2018-03-10  Yusuke Suzuki  <utatane.tea@gmail.com>
+
         B3::reduceStrength should canonicalize integer comparisons
         https://bugs.webkit.org/show_bug.cgi?id=150958
 
index 8ced84f..de6a8c6 100644 (file)
@@ -2004,6 +2004,11 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         ASSERT(node->child3().useKind() == StringUse);
         forNode(node).setType(m_graph, SpecOther | SpecArray);
         break;
+
+    case RegExpMatchFastGlobal:
+        ASSERT(node->child2().useKind() == StringUse);
+        forNode(node).setType(m_graph, SpecOther | SpecArray);
+        break;
             
     case StringReplace:
     case StringReplaceRegExp:
index 59577e6..6c54eb9 100644 (file)
@@ -1512,6 +1512,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         return;
 
     case RegExpExecNonGlobalOrSticky:
+    case RegExpMatchFastGlobal:
         read(RegExpState);
         write(RegExpState);
         return;
index 2cb10b2..02079f9 100644 (file)
@@ -141,6 +141,7 @@ bool doesGC(Graph& graph, Node* node)
     case RegExpExecNonGlobalOrSticky:
     case RegExpTest:
     case RegExpMatchFast:
+    case RegExpMatchFastGlobal:
     case CompareLess:
     case CompareLessEq:
     case CompareGreater:
index 56ec2a9..142943d 100644 (file)
@@ -1673,6 +1673,7 @@ private:
         case SetRegExpObjectLastIndex:
         case RecordRegExpCachedResult:
         case RegExpExecNonGlobalOrSticky:
+        case RegExpMatchFastGlobal:
             // 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 7d71566..159a9bb 100644 (file)
@@ -121,6 +121,7 @@ ExitMode mayExitImpl(Graph& graph, Node* node, StateType& state)
     case NewRegexp:
     case ToNumber:
     case RegExpExecNonGlobalOrSticky:
+    case RegExpMatchFastGlobal:
         result = ExitsForExceptions;
         break;
 
index 81fc240..3a9180d 100644 (file)
@@ -234,6 +234,16 @@ void Node::convertToRegExpExecNonGlobalOrSticky(FrozenValue* regExp)
     m_opInfo = regExp;
 }
 
+void Node::convertToRegExpMatchFastGlobal(FrozenValue* regExp)
+{
+    ASSERT(op() == RegExpMatchFast);
+    setOpAndDefaultFlags(RegExpMatchFastGlobal);
+    children.child1() = Edge(children.child1().node(), KnownCellUse);
+    children.child2() = Edge(children.child3().node(), StringUse);
+    children.child3() = Edge();
+    m_opInfo = regExp;
+}
+
 String Node::tryGetString(Graph& graph)
 {
     if (hasConstant())
index 21fab2f..c8eb786 100644 (file)
@@ -738,6 +738,7 @@ public:
     void convertToCallDOM(Graph&);
 
     void convertToRegExpExecNonGlobalOrSticky(FrozenValue* regExp);
+    void convertToRegExpMatchFastGlobal(FrozenValue* regExp);
 
     void convertToSetRegExpObjectLastIndex()
     {
@@ -1595,6 +1596,7 @@ public:
         case RegExpExecNonGlobalOrSticky:
         case RegExpTest:
         case RegExpMatchFast:
+        case RegExpMatchFastGlobal:
         case GetGlobalVar:
         case GetGlobalLexicalVariable:
         case StringReplace:
@@ -1676,6 +1678,7 @@ public:
         case DirectConstruct:
         case DirectTailCallInlinedCaller:
         case RegExpExecNonGlobalOrSticky:
+        case RegExpMatchFastGlobal:
             return true;
         default:
             return false;
index b5b56b2..ed9d6e6 100644 (file)
@@ -274,6 +274,7 @@ namespace JSC { namespace DFG {
     macro(RegExpExecNonGlobalOrSticky, NodeResultJS) \
     macro(RegExpTest, NodeResultJS | NodeMustGenerate) \
     macro(RegExpMatchFast, NodeResultJS | NodeMustGenerate) \
+    macro(RegExpMatchFastGlobal, NodeResultJS) \
     macro(StringReplace, NodeResultJS | NodeMustGenerate) \
     macro(StringReplaceRegExp, NodeResultJS | NodeMustGenerate) \
     \
index b1dadd2..a66f53c 100644 (file)
@@ -65,7 +65,7 @@
 #include "ParseInt.h"
 #include "RegExpConstructor.h"
 #include "RegExpMatchesArray.h"
-#include "RegExpObject.h"
+#include "RegExpObjectInlines.h"
 #include "Repatch.h"
 #include "ScopedArguments.h"
 #include "StringConstructor.h"
@@ -1099,6 +1099,40 @@ EncodedJSValue JIT_OPERATION operationRegExpMatchFastString(ExecState* exec, JSG
     return JSValue::encode(regExpObject->matchGlobal(exec, globalObject, argument));
 }
 
+EncodedJSValue JIT_OPERATION operationRegExpMatchFastGlobalString(ExecState* exec, JSGlobalObject* globalObject, RegExp* regExp, JSString* string)
+{
+    SuperSamplerScope superSamplerScope(false);
+
+    VM& vm = globalObject->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    ASSERT(regExp->global());
+
+    String s = string->value(exec);
+    RETURN_IF_EXCEPTION(scope, { });
+
+    RegExpConstructor* regExpConstructor = globalObject->regExpConstructor();
+
+    if (regExp->unicode()) {
+        unsigned stringLength = s.length();
+        scope.release();
+        return JSValue::encode(collectMatches(
+            vm, exec, string, s, regExpConstructor, regExp,
+            [&] (size_t end) -> size_t {
+                return advanceStringUnicode(s, stringLength, end);
+            }));
+    }
+
+    scope.release();
+    return JSValue::encode(collectMatches(
+        vm, exec, string, s, regExpConstructor, regExp,
+        [&] (size_t end) -> size_t {
+            return end + 1;
+        }));
+}
+
 EncodedJSValue JIT_OPERATION operationParseIntNoRadixGeneric(ExecState* exec, EncodedJSValue value)
 {
     VM& vm = exec->vm();
index 0529a27..7add59d 100644 (file)
@@ -155,6 +155,7 @@ EncodedJSValue JIT_OPERATION operationRegExpExecString(ExecState*, JSGlobalObjec
 EncodedJSValue JIT_OPERATION operationRegExpExec(ExecState*, JSGlobalObject*, RegExpObject*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationRegExpExecGeneric(ExecState*, JSGlobalObject*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationRegExpExecNonGlobalOrSticky(ExecState*, JSGlobalObject*, RegExp*, JSString*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationRegExpMatchFastGlobalString(ExecState*, JSGlobalObject*, RegExp*, JSString*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationRegExpMatchFastString(ExecState*, JSGlobalObject*, RegExpObject*, JSString*) WTF_INTERNAL;
 // These comparisons return a boolean within a size_t such that the value is zero extended to fill the register.
 size_t JIT_OPERATION operationRegExpTestString(ExecState*, JSGlobalObject*, RegExpObject*, JSString*) WTF_INTERNAL;
index 0e096de..12215b6 100644 (file)
@@ -699,6 +699,7 @@ private:
         case RegExpExecNonGlobalOrSticky:
         case RegExpTest:
         case RegExpMatchFast:
+        case RegExpMatchFastGlobal:
         case StringReplace:
         case StringReplaceRegExp:
         case GetById:
index 02c71a5..e42c2f3 100644 (file)
@@ -265,6 +265,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case RegExpExecNonGlobalOrSticky:
     case RegExpTest:
     case RegExpMatchFast:
+    case RegExpMatchFastGlobal:
     case CompareLess:
     case CompareLessEq:
     case CompareGreater:
index 25a5686..a21f017 100644 (file)
@@ -10687,6 +10687,26 @@ void SpeculativeJIT::compileRegExpExecNonGlobalOrSticky(Node* node)
     jsValueResult(resultRegs, node);
 }
 
+void SpeculativeJIT::compileRegExpMatchFastGlobal(Node* node)
+{
+    SpeculateCellOperand globalObject(this, node->child1());
+    SpeculateCellOperand argument(this, node->child2());
+    GPRReg globalObjectGPR = globalObject.gpr();
+    GPRReg argumentGPR = argument.gpr();
+
+    speculateString(node->child2(), argumentGPR);
+
+    flushRegisters();
+    JSValueRegsFlushedCallResult result(this);
+    JSValueRegs resultRegs = result.regs();
+    callOperation(
+        operationRegExpMatchFastGlobalString, resultRegs,
+        globalObjectGPR, TrustedImmPtr(node->cellOperand()), argumentGPR);
+    m_jit.exceptionCheck();
+
+    jsValueResult(resultRegs, node);
+}
+
 void SpeculativeJIT::compileRegExpMatchFast(Node* node)
 {
     SpeculateCellOperand globalObject(this, node->child1());
index e99612a..6750d49 100644 (file)
@@ -1400,6 +1400,7 @@ public:
     void compileRegExpExec(Node*);
     void compileRegExpExecNonGlobalOrSticky(Node*);
     void compileRegExpMatchFast(Node*);
+    void compileRegExpMatchFastGlobal(Node*);
     void compileRegExpTest(Node*);
     void compileStringReplace(Node*);
     void compileIsObjectOrNull(Node*);
index 45e2b6f..4391454 100644 (file)
@@ -3160,6 +3160,11 @@ void SpeculativeJIT::compile(Node* node)
         compileRegExpExecNonGlobalOrSticky(node);
         break;
     }
+
+    case RegExpMatchFastGlobal: {
+        compileRegExpMatchFastGlobal(node);
+        break;
+    }
         
     case RegExpTest: {
         compileRegExpTest(node);
index 63097d7..b6bd192 100644 (file)
@@ -3428,6 +3428,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case RegExpMatchFastGlobal: {
+        compileRegExpMatchFastGlobal(node);
+        break;
+    }
+
     case RegExpTest: {
         compileRegExpTest(node);
         break;
index c190e27..5c1be68 100644 (file)
@@ -495,12 +495,30 @@ private:
                 regExp = m_node->castOperand<RegExp*>();
 
             if (m_node->op() == RegExpMatchFast) {
-                if (!regExp->global()) {
-                    m_node->setOp(RegExpExec);
+                if (regExp->global()) {
+                    if (regExp->sticky())
+                        break;
+                    if (m_node->child3().useKind() != StringUse)
+                        break;
+                    NodeOrigin origin = m_node->origin;
+                    m_insertionSet.insertNode(
+                        m_nodeIndex, SpecNone, Check, origin, m_node->children.justChecks());
+                    m_insertionSet.insertNode(
+                        m_nodeIndex, SpecNone, SetRegExpObjectLastIndex, origin,
+                        OpInfo(false),
+                        Edge(regExpObjectNode, RegExpObjectUse),
+                        m_insertionSet.insertConstantForUse(
+                            m_nodeIndex, origin, jsNumber(0), UntypedUse));
+                    origin = origin.withInvalidExit();
+                    m_node->convertToRegExpMatchFastGlobal(m_graph.freeze(regExp));
+                    m_node->origin = origin;
                     m_changed = true;
-                    // Continue performing strength reduction onto RegExpExec node.
-                } else
                     break;
+                }
+
+                m_node->setOp(RegExpExec);
+                m_changed = true;
+                // Continue performing strength reduction onto RegExpExec node.
             }
 
             ASSERT(m_node->op() != RegExpMatchFast);
index 1a3edf5..754a1e6 100644 (file)
@@ -287,6 +287,7 @@ inline CapabilityLevel canCompile(Node* node)
     case RegExpExecNonGlobalOrSticky:
     case RegExpTest:
     case RegExpMatchFast:
+    case RegExpMatchFastGlobal:
     case NewRegexp:
     case StringReplace:
     case StringReplaceRegExp: 
index 6479bd7..e12396e 100644 (file)
@@ -1211,6 +1211,9 @@ private:
         case RegExpMatchFast:
             compileRegExpMatchFast();
             break;
+        case RegExpMatchFastGlobal:
+            compileRegExpMatchFastGlobal();
+            break;
         case NewRegexp:
             compileNewRegexp();
             break;
@@ -10684,6 +10687,15 @@ private:
         setJSValue(result);
     }
 
+    void compileRegExpMatchFastGlobal()
+    {
+        LValue globalObject = lowCell(m_node->child1());
+        LValue argument = lowString(m_node->child2());
+        LValue result = vmCall(
+            Int64, m_out.operation(operationRegExpMatchFastGlobalString), m_callFrame, globalObject, frozenPointer(m_node->cellOperand()), argument);
+        setJSValue(result);
+    }
+
     void compileRegExpTest()
     {
         LValue globalObject = lowCell(m_node->child1());
index 9a46629..3a5cf37 100644 (file)
@@ -177,76 +177,6 @@ MatchResult RegExpObject::match(ExecState* exec, JSGlobalObject* globalObject, J
     return matchInline(exec, globalObject, string);
 }
 
-template<typename FixEndFunc>
-JSValue collectMatches(VM& vm, ExecState* exec, JSString* string, const String& s, RegExpConstructor* constructor, RegExp* regExp, const FixEndFunc& fixEnd)
-{
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    MatchResult result = constructor->performMatch(vm, regExp, string, s, 0);
-    if (!result)
-        return jsNull();
-    
-    static unsigned maxSizeForDirectPath = 100000;
-    
-    JSArray* array = constructEmptyArray(exec, nullptr);
-    RETURN_IF_EXCEPTION(scope, { });
-
-    bool hasException = false;
-    auto iterate = [&] () {
-        size_t end = result.end;
-        size_t length = end - result.start;
-        array->push(exec, JSRopeString::createSubstringOfResolved(vm, string, result.start, length));
-        if (UNLIKELY(scope.exception())) {
-            hasException = true;
-            return;
-        }
-        if (!length)
-            end = fixEnd(end);
-        result = constructor->performMatch(vm, regExp, string, s, end);
-    };
-    
-    do {
-        if (array->length() >= maxSizeForDirectPath) {
-            // First do a throw-away match to see how many matches we'll get.
-            unsigned matchCount = 0;
-            MatchResult savedResult = result;
-            do {
-                if (array->length() + matchCount > MAX_STORAGE_VECTOR_LENGTH) {
-                    throwOutOfMemoryError(exec, scope);
-                    return jsUndefined();
-                }
-                
-                size_t end = result.end;
-                matchCount++;
-                if (result.empty())
-                    end = fixEnd(end);
-                
-                // Using RegExpConstructor::performMatch() instead of calling RegExp::match()
-                // directly is a surprising but profitable choice: it means that when we do OOM, we
-                // will leave the cached result in the state it ought to have had just before the
-                // OOM! On the other hand, if this loop concludes that the result is small enough,
-                // then the iterate() loop below will overwrite the cached result anyway.
-                result = constructor->performMatch(vm, regExp, string, s, end);
-            } while (result);
-            
-            // OK, we have a sensible number of matches. Now we can create them for reals.
-            result = savedResult;
-            do {
-                iterate();
-                EXCEPTION_ASSERT(!!scope.exception() == hasException);
-                if (UNLIKELY(hasException))
-                    return { };
-            } while (result);
-            
-            return array;
-        }
-        
-        iterate();
-    } while (result);
-    
-    return array;
-}
-
 JSValue RegExpObject::matchGlobal(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
 {
     VM& vm = globalObject->vm();
index 4f7ab53..a316cba 100644 (file)
@@ -116,8 +116,6 @@ public:
         return sizeof(RegExpObject);
     }
 
-    static unsigned advanceStringUnicode(String, unsigned length, unsigned currentIndex);
-
 protected:
     JS_EXPORT_PRIVATE RegExpObject(VM&, Structure*, RegExp*);
     JS_EXPORT_PRIVATE void finishCreation(VM&);
index 01514f3..23006f3 100644 (file)
@@ -60,7 +60,7 @@ ALWAYS_INLINE unsigned getRegExpObjectLastIndexAsUnsigned(
     return lastIndex;
 }
 
-JSValue RegExpObject::execInline(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
+inline JSValue RegExpObject::execInline(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
 {
     VM& vm = globalObject->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
@@ -99,7 +99,7 @@ JSValue RegExpObject::execInline(ExecState* exec, JSGlobalObject* globalObject,
 }
 
 // Shared implementation used by test and exec.
-MatchResult RegExpObject::matchInline(
+inline MatchResult RegExpObject::matchInline(
     ExecState* exec, JSGlobalObject* globalObject, JSString* string)
 {
     VM& vm = globalObject->vm();
@@ -124,7 +124,7 @@ MatchResult RegExpObject::matchInline(
     return result;
 }
 
-unsigned RegExpObject::advanceStringUnicode(String s, unsigned length, unsigned currentIndex)
+inline unsigned advanceStringUnicode(String s, unsigned length, unsigned currentIndex)
 {
     if (currentIndex + 1 >= length)
         return currentIndex + 1;
@@ -140,4 +140,74 @@ unsigned RegExpObject::advanceStringUnicode(String s, unsigned length, unsigned
     return currentIndex + 2;
 }
 
+template<typename FixEndFunc>
+JSValue collectMatches(VM& vm, ExecState* exec, JSString* string, const String& s, RegExpConstructor* constructor, RegExp* regExp, const FixEndFunc& fixEnd)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    MatchResult result = constructor->performMatch(vm, regExp, string, s, 0);
+    if (!result)
+        return jsNull();
+    
+    static unsigned maxSizeForDirectPath = 100000;
+    
+    JSArray* array = constructEmptyArray(exec, nullptr);
+    RETURN_IF_EXCEPTION(scope, { });
+
+    bool hasException = false;
+    auto iterate = [&] () {
+        size_t end = result.end;
+        size_t length = end - result.start;
+        array->push(exec, JSRopeString::createSubstringOfResolved(vm, string, result.start, length));
+        if (UNLIKELY(scope.exception())) {
+            hasException = true;
+            return;
+        }
+        if (!length)
+            end = fixEnd(end);
+        result = constructor->performMatch(vm, regExp, string, s, end);
+    };
+    
+    do {
+        if (array->length() >= maxSizeForDirectPath) {
+            // First do a throw-away match to see how many matches we'll get.
+            unsigned matchCount = 0;
+            MatchResult savedResult = result;
+            do {
+                if (array->length() + matchCount > MAX_STORAGE_VECTOR_LENGTH) {
+                    throwOutOfMemoryError(exec, scope);
+                    return jsUndefined();
+                }
+                
+                size_t end = result.end;
+                matchCount++;
+                if (result.empty())
+                    end = fixEnd(end);
+                
+                // Using RegExpConstructor::performMatch() instead of calling RegExp::match()
+                // directly is a surprising but profitable choice: it means that when we do OOM, we
+                // will leave the cached result in the state it ought to have had just before the
+                // OOM! On the other hand, if this loop concludes that the result is small enough,
+                // then the iterate() loop below will overwrite the cached result anyway.
+                result = constructor->performMatch(vm, regExp, string, s, end);
+            } while (result);
+            
+            // OK, we have a sensible number of matches. Now we can create them for reals.
+            result = savedResult;
+            do {
+                iterate();
+                EXCEPTION_ASSERT(!!scope.exception() == hasException);
+                if (UNLIKELY(hasException))
+                    return { };
+            } while (result);
+            
+            return array;
+        }
+        
+        iterate();
+    } while (result);
+    
+    return array;
+}
+
 } // namespace JSC
index 37a9dec..d321e2d 100644 (file)
@@ -508,7 +508,7 @@ static inline unsigned advanceStringIndex(String str, unsigned strSize, unsigned
 {
     if (!isUnicode)
         return ++index;
-    return RegExpObject::advanceStringUnicode(str, strSize, index);
+    return advanceStringUnicode(str, strSize, index);
 }
 
 enum SplitControl {