Diff
Modified: trunk/JSTests/ChangeLog (229513 => 229514)
--- trunk/JSTests/ChangeLog 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/JSTests/ChangeLog 2018-03-11 07:20:29 UTC (rev 229514)
@@ -1,3 +1,22 @@
+2018-03-10 Yusuke Suzuki <[email protected]>
+
+ [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 <[email protected]>
Disable test stress/var-injection-cache-invalidation.js on systems with limited memory
Added: trunk/JSTests/microbenchmarks/regexp-u-global-es5.js (0 => 229514)
--- trunk/JSTests/microbenchmarks/regexp-u-global-es5.js (rev 0)
+++ trunk/JSTests/microbenchmarks/regexp-u-global-es5.js 2018-03-11 07:20:29 UTC (rev 229514)
@@ -0,0 +1,7 @@
+function fn() {
+ return '𠮷'.match(/^.$/g);
+}
+noInline(fn);
+
+for (var i = 0; i < 1e6; ++i)
+ fn();
Added: trunk/JSTests/microbenchmarks/regexp-u-global-es6.js (0 => 229514)
--- trunk/JSTests/microbenchmarks/regexp-u-global-es6.js (rev 0)
+++ trunk/JSTests/microbenchmarks/regexp-u-global-es6.js 2018-03-11 07:20:29 UTC (rev 229514)
@@ -0,0 +1,7 @@
+function fn() {
+ return '𠮷'.match(/^.$/ug);
+}
+noInline(fn);
+
+for (var i = 0; i < 1e6; ++i)
+ fn();
Added: trunk/JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match-at-osr-exit.js (0 => 229514)
--- trunk/JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match-at-osr-exit.js (rev 0)
+++ trunk/JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match-at-osr-exit.js 2018-03-11 07:20:29 UTC (rev 229514)
@@ -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");
Added: trunk/JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match.js (0 => 229514)
--- trunk/JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match.js (rev 0)
+++ trunk/JSTests/stress/materialized-regexp-has-correct-last-index-set-by-match.js 2018-03-11 07:20:29 UTC (rev 229514)
@@ -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);
+}
Modified: trunk/Source/_javascript_Core/ChangeLog (229513 => 229514)
--- trunk/Source/_javascript_Core/ChangeLog 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/ChangeLog 2018-03-11 07:20:29 UTC (rev 229514)
@@ -1,5 +1,74 @@
2018-03-10 Yusuke Suzuki <[email protected]>
+ [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 <[email protected]>
+
B3::reduceStrength should canonicalize integer comparisons
https://bugs.webkit.org/show_bug.cgi?id=150958
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2018-03-11 07:20:29 UTC (rev 229514)
@@ -2004,6 +2004,11 @@
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:
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2018-03-11 07:20:29 UTC (rev 229514)
@@ -1512,6 +1512,7 @@
return;
case RegExpExecNonGlobalOrSticky:
+ case RegExpMatchFastGlobal:
read(RegExpState);
write(RegExpState);
return;
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -141,6 +141,7 @@
case RegExpExecNonGlobalOrSticky:
case RegExpTest:
case RegExpMatchFast:
+ case RegExpMatchFastGlobal:
case CompareLess:
case CompareLessEq:
case CompareGreater:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -1673,6 +1673,7 @@
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.
Modified: trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -121,6 +121,7 @@
case NewRegexp:
case ToNumber:
case RegExpExecNonGlobalOrSticky:
+ case RegExpMatchFastGlobal:
result = ExitsForExceptions;
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGNode.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -234,6 +234,16 @@
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())
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2018-03-11 07:20:29 UTC (rev 229514)
@@ -738,6 +738,7 @@
void convertToCallDOM(Graph&);
void convertToRegExpExecNonGlobalOrSticky(FrozenValue* regExp);
+ void convertToRegExpMatchFastGlobal(FrozenValue* regExp);
void convertToSetRegExpObjectLastIndex()
{
@@ -1595,6 +1596,7 @@
case RegExpExecNonGlobalOrSticky:
case RegExpTest:
case RegExpMatchFast:
+ case RegExpMatchFastGlobal:
case GetGlobalVar:
case GetGlobalLexicalVariable:
case StringReplace:
@@ -1676,6 +1678,7 @@
case DirectConstruct:
case DirectTailCallInlinedCaller:
case RegExpExecNonGlobalOrSticky:
+ case RegExpMatchFastGlobal:
return true;
default:
return false;
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2018-03-11 07:20:29 UTC (rev 229514)
@@ -274,6 +274,7 @@
macro(RegExpExecNonGlobalOrSticky, NodeResultJS) \
macro(RegExpTest, NodeResultJS | NodeMustGenerate) \
macro(RegExpMatchFast, NodeResultJS | NodeMustGenerate) \
+ macro(RegExpMatchFastGlobal, NodeResultJS) \
macro(StringReplace, NodeResultJS | NodeMustGenerate) \
macro(StringReplaceRegExp, NodeResultJS | NodeMustGenerate) \
\
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -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 @@
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();
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.h 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h 2018-03-11 07:20:29 UTC (rev 229514)
@@ -155,6 +155,7 @@
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;
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -699,6 +699,7 @@
case RegExpExecNonGlobalOrSticky:
case RegExpTest:
case RegExpMatchFast:
+ case RegExpMatchFastGlobal:
case StringReplace:
case StringReplaceRegExp:
case GetById:
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2018-03-11 07:20:29 UTC (rev 229514)
@@ -265,6 +265,7 @@
case RegExpExecNonGlobalOrSticky:
case RegExpTest:
case RegExpMatchFast:
+ case RegExpMatchFastGlobal:
case CompareLess:
case CompareLessEq:
case CompareGreater:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -10687,6 +10687,26 @@
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());
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2018-03-11 07:20:29 UTC (rev 229514)
@@ -1400,6 +1400,7 @@
void compileRegExpExec(Node*);
void compileRegExpExecNonGlobalOrSticky(Node*);
void compileRegExpMatchFast(Node*);
+ void compileRegExpMatchFastGlobal(Node*);
void compileRegExpTest(Node*);
void compileStringReplace(Node*);
void compileIsObjectOrNull(Node*);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -3160,6 +3160,11 @@
compileRegExpExecNonGlobalOrSticky(node);
break;
}
+
+ case RegExpMatchFastGlobal: {
+ compileRegExpMatchFastGlobal(node);
+ break;
+ }
case RegExpTest: {
compileRegExpTest(node);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -3428,6 +3428,11 @@
break;
}
+ case RegExpMatchFastGlobal: {
+ compileRegExpMatchFastGlobal(node);
+ break;
+ }
+
case RegExpTest: {
compileRegExpTest(node);
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -495,12 +495,30 @@
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);
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -287,6 +287,7 @@
case RegExpExecNonGlobalOrSticky:
case RegExpTest:
case RegExpMatchFast:
+ case RegExpMatchFastGlobal:
case NewRegexp:
case StringReplace:
case StringReplaceRegExp:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -1211,6 +1211,9 @@
case RegExpMatchFast:
compileRegExpMatchFast();
break;
+ case RegExpMatchFastGlobal:
+ compileRegExpMatchFastGlobal();
+ break;
case NewRegexp:
compileNewRegexp();
break;
@@ -10684,6 +10687,15 @@
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());
Modified: trunk/Source/_javascript_Core/runtime/RegExpObject.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/runtime/RegExpObject.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/runtime/RegExpObject.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -177,76 +177,6 @@
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();
Modified: trunk/Source/_javascript_Core/runtime/RegExpObject.h (229513 => 229514)
--- trunk/Source/_javascript_Core/runtime/RegExpObject.h 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/runtime/RegExpObject.h 2018-03-11 07:20:29 UTC (rev 229514)
@@ -116,8 +116,6 @@
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&);
Modified: trunk/Source/_javascript_Core/runtime/RegExpObjectInlines.h (229513 => 229514)
--- trunk/Source/_javascript_Core/runtime/RegExpObjectInlines.h 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/runtime/RegExpObjectInlines.h 2018-03-11 07:20:29 UTC (rev 229514)
@@ -60,7 +60,7 @@
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 @@
}
// 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 @@
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 @@
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
Modified: trunk/Source/_javascript_Core/runtime/RegExpPrototype.cpp (229513 => 229514)
--- trunk/Source/_javascript_Core/runtime/RegExpPrototype.cpp 2018-03-11 07:16:15 UTC (rev 229513)
+++ trunk/Source/_javascript_Core/runtime/RegExpPrototype.cpp 2018-03-11 07:20:29 UTC (rev 229514)
@@ -508,7 +508,7 @@
{
if (!isUnicode)
return ++index;
- return RegExpObject::advanceStringUnicode(str, strSize, index);
+ return advanceStringUnicode(str, strSize, index);
}
enum SplitControl {