Diff
Modified: trunk/JSTests/ChangeLog (227106 => 227107)
--- trunk/JSTests/ChangeLog 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/JSTests/ChangeLog 2018-01-18 04:17:32 UTC (rev 227107)
@@ -1,3 +1,48 @@
+2018-01-17 Yusuke Suzuki <utatane....@gmail.com>
+
+ [DFG][FTL] Introduce PhantomNewRegexp and RegExpExecNonGlobalOrSticky
+ https://bugs.webkit.org/show_bug.cgi?id=181535
+
+ Reviewed by Saam Barati.
+
+ * stress/inserted-recovery-with-set-last-index.js: Added.
+ (shouldBe):
+ (foo):
+ * stress/materialize-regexp-at-osr-exit.js: Added.
+ (shouldBe):
+ (test):
+ * stress/materialize-regexp-cyclic-regexp-at-osr-exit.js: Added.
+ (shouldBe):
+ (test):
+ * stress/materialize-regexp-cyclic-regexp.js: Added.
+ (shouldBe):
+ (test):
+ (i.switch):
+ * stress/materialize-regexp-cyclic.js: Added.
+ (shouldBe):
+ (test):
+ (i.switch):
+ * stress/materialize-regexp-referenced-from-phantom-regexp-cyclic.js: Added.
+ (bar):
+ (foo):
+ (test):
+ * stress/materialize-regexp-referenced-from-phantom-regexp.js: Added.
+ (bar):
+ (foo):
+ (test):
+ * stress/materialize-regexp.js: Added.
+ (shouldBe):
+ (test):
+ * stress/phantom-regexp-regexp-exec.js: Added.
+ (shouldBe):
+ (test):
+ * stress/phantom-regexp-string-match.js: Added.
+ (shouldBe):
+ (test):
+ * stress/regexp-last-index-sinking.js: Added.
+ (shouldBe):
+ (test):
+
2018-01-17 Saam Barati <sbar...@apple.com>
Disable Atomics when SharedArrayBuffer isn’t enabled
Added: trunk/JSTests/stress/inserted-recovery-with-set-last-index.js (0 => 227107)
--- trunk/JSTests/stress/inserted-recovery-with-set-last-index.js (rev 0)
+++ trunk/JSTests/stress/inserted-recovery-with-set-last-index.js 2018-01-18 04:17:32 UTC (rev 227107)
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function foo(p) {
+ var o = /Hello/;
+ if (p) {
+ var res = /World/;
+ res.lastIndex = o;
+ return res;
+ }
+}
+
+noInline(foo);
+
+var array = new Array(1000);
+for (var i = 0; i < 4000000; ++i) {
+ var o = foo(i & 0x1);
+ if (i & 0x1) {
+ shouldBe(o instanceof RegExp, true);
+ shouldBe(o.toString(), "/World/");
+ shouldBe(o.lastIndex.toString(), "/Hello/");
+ }
+ array[i % array.length] = o;
+}
+
Added: trunk/JSTests/stress/materialize-regexp-at-osr-exit.js (0 => 227107)
--- trunk/JSTests/stress/materialize-regexp-at-osr-exit.js (rev 0)
+++ trunk/JSTests/stress/materialize-regexp-at-osr-exit.js 2018-01-18 04:17:32 UTC (rev 227107)
@@ -0,0 +1,24 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test(flag)
+{
+ var regexp = /hello world/;
+ regexp.lastIndex = "Cocoa";
+ if (flag) {
+ OSRExit();
+ return regexp;
+ }
+ return regexp.lastIndex;
+}
+noInline(test);
+
+for (var i = 0; i < 1e5; ++i)
+ shouldBe(test(false), "Cocoa");
+
+var regexp = test(true);
+shouldBe(regexp instanceof RegExp, true);
+shouldBe(regexp.lastIndex, "Cocoa");
Added: trunk/JSTests/stress/materialize-regexp-cyclic-regexp-at-osr-exit.js (0 => 227107)
--- trunk/JSTests/stress/materialize-regexp-cyclic-regexp-at-osr-exit.js (rev 0)
+++ trunk/JSTests/stress/materialize-regexp-cyclic-regexp-at-osr-exit.js 2018-01-18 04:17:32 UTC (rev 227107)
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test(num)
+{
+ var regexp = /hello world/;
+ var world = /World/;
+ regexp.lastIndex = world;
+ world.lastIndex = regexp;
+ if (num === 0) {
+ OSRExit();
+ return regexp;
+ }
+ return 42;
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+ shouldBe(test(1), 42);
+
+var regexp = test(0);
+shouldBe(regexp instanceof RegExp, true);
+shouldBe(regexp.toString(), "/hello world/");
+shouldBe(regexp.lastIndex instanceof RegExp, true);
+shouldBe(regexp.lastIndex.toString(), "/World/");
Added: trunk/JSTests/stress/materialize-regexp-cyclic-regexp.js (0 => 227107)
--- trunk/JSTests/stress/materialize-regexp-cyclic-regexp.js (rev 0)
+++ trunk/JSTests/stress/materialize-regexp-cyclic-regexp.js 2018-01-18 04:17:32 UTC (rev 227107)
@@ -0,0 +1,46 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test(num)
+{
+ var regexp = /hello world/;
+ var world = /World/;
+ regexp.lastIndex = world;
+ world.lastIndex = regexp;
+ if (num === 0)
+ return regexp;
+ if (num === 1)
+ return regexp.lastIndex;
+ return regexp.lastIndex.lastIndex;
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i) {
+ var num = i % 3;
+ switch (num) {
+ case 0:
+ var regexp = test(num);
+ shouldBe(regexp instanceof RegExp, true);
+ shouldBe(regexp.toString(), "/hello world/");
+ shouldBe(regexp.lastIndex instanceof RegExp, true);
+ shouldBe(regexp.lastIndex.toString(), "/World/");
+ break;
+ case 1:
+ var regexp = test(num);
+ shouldBe(regexp instanceof RegExp, true);
+ shouldBe(regexp.toString(), "/World/");
+ shouldBe(regexp.lastIndex instanceof RegExp, true);
+ shouldBe(regexp.lastIndex.toString(), "/hello world/");
+ break;
+ case 2:
+ var regexp = test(num);
+ shouldBe(regexp instanceof RegExp, true);
+ shouldBe(regexp.toString(), "/hello world/");
+ shouldBe(regexp.lastIndex instanceof RegExp, true);
+ shouldBe(regexp.lastIndex.toString(), "/World/");
+ break;
+ }
+}
Added: trunk/JSTests/stress/materialize-regexp-cyclic.js (0 => 227107)
--- trunk/JSTests/stress/materialize-regexp-cyclic.js (rev 0)
+++ trunk/JSTests/stress/materialize-regexp-cyclic.js 2018-01-18 04:17:32 UTC (rev 227107)
@@ -0,0 +1,39 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test(num)
+{
+ var regexp = /hello world/;
+ regexp.lastIndex = { ok: regexp, value: 42 };
+ if (num === 0)
+ return regexp;
+ if (num === 1)
+ return regexp.lastIndex;
+ return regexp.lastIndex.value;
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i) {
+ var num = i % 3;
+ switch (num) {
+ case 0:
+ var regexp = test(num);
+ shouldBe(regexp instanceof RegExp, true);
+ shouldBe(typeof regexp.lastIndex, "object");
+ shouldBe(regexp.lastIndex.ok, regexp);
+ break;
+ case 1:
+ var object = test(num);
+ shouldBe(object.value, 42);
+ shouldBe(object.ok instanceof RegExp, true);
+ shouldBe(object.ok.lastIndex, object);
+ break;
+ case 2:
+ var value = test(num);
+ shouldBe(value, 42);
+ break;
+ }
+}
Added: trunk/JSTests/stress/materialize-regexp-referenced-from-phantom-regexp-cyclic.js (0 => 227107)
--- trunk/JSTests/stress/materialize-regexp-referenced-from-phantom-regexp-cyclic.js (rev 0)
+++ trunk/JSTests/stress/materialize-regexp-referenced-from-phantom-regexp-cyclic.js 2018-01-18 04:17:32 UTC (rev 227107)
@@ -0,0 +1,43 @@
+function bar()
+{
+}
+
+noInline(bar);
+
+function foo(p, x)
+{
+ var a = /Hello/;
+ a.lastIndex = 1;
+ var b = /World/;
+ b.lastIndex = a;
+ var c = /World/;
+ c.lastIndex = a;
+ var d = /Cocoa/;
+ d.lastIndex = c;
+ a.lastIndex = d;
+
+ if (!p)
+ return 0;
+
+ bar(b);
+
+ x += 2000000000;
+
+ c.lastIndex.lastIndex = 42;
+ return b.lastIndex.lastIndex;
+}
+
+noInline(foo);
+
+function test(x)
+{
+ var result = foo(true, x);
+ if (result != 42)
+ throw "Error: bad result: " + result;
+}
+
+for (var i = 0; i < 100000; ++i)
+ test(0);
+
+test(2000000000);
+
Added: trunk/JSTests/stress/materialize-regexp-referenced-from-phantom-regexp.js (0 => 227107)
--- trunk/JSTests/stress/materialize-regexp-referenced-from-phantom-regexp.js (rev 0)
+++ trunk/JSTests/stress/materialize-regexp-referenced-from-phantom-regexp.js 2018-01-18 04:17:32 UTC (rev 227107)
@@ -0,0 +1,40 @@
+function bar()
+{
+}
+
+noInline(bar);
+
+function foo(p, x)
+{
+ var a = /Hello/;
+ a.lastIndex = 1;
+ var b = /World/;
+ b.lastIndex = a;
+ var c = /World/;
+ c.lastIndex = a;
+
+ if (!p)
+ return 0;
+
+ bar(b);
+
+ x += 2000000000;
+
+ c.lastIndex.lastIndex = 42;
+ return b.lastIndex.lastIndex;
+}
+
+noInline(foo);
+
+function test(x)
+{
+ var result = foo(true, x);
+ if (result != 42)
+ throw "Error: bad result: " + result;
+}
+
+for (var i = 0; i < 100000; ++i)
+ test(0);
+
+test(2000000000);
+
Added: trunk/JSTests/stress/materialize-regexp.js (0 => 227107)
--- trunk/JSTests/stress/materialize-regexp.js (rev 0)
+++ trunk/JSTests/stress/materialize-regexp.js 2018-01-18 04:17:32 UTC (rev 227107)
@@ -0,0 +1,24 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test(flag)
+{
+ var regexp = /hello world/;
+ regexp.lastIndex = "Cocoa";
+ if (flag)
+ return regexp;
+ return regexp.lastIndex;
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i) {
+ if (i & 0x1) {
+ var regexp = test(true);
+ shouldBe(regexp instanceof RegExp, true);
+ shouldBe(regexp.lastIndex, "Cocoa");
+ } else
+ shouldBe(test(false), "Cocoa");
+}
Added: trunk/JSTests/stress/phantom-regexp-regexp-exec.js (0 => 227107)
--- trunk/JSTests/stress/phantom-regexp-regexp-exec.js (rev 0)
+++ trunk/JSTests/stress/phantom-regexp-regexp-exec.js 2018-01-18 04:17:32 UTC (rev 227107)
@@ -0,0 +1,26 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test(string, flag)
+{
+ var regexp = /oa/;
+ var result = regexp.exec(string);
+ if (flag)
+ return regexp;
+ return result;
+}
+noInline(test);
+
+for (var i = 0; i < 1e5; ++i) {
+ if (i & 1) {
+ var result = test("Cocoa", true);
+ shouldBe(result instanceof RegExp, true);
+ } else {
+ var result = test("Cocoa", false);
+ shouldBe(result.input, "Cocoa");
+ shouldBe(result[0], "oa");
+ }
+}
Added: trunk/JSTests/stress/phantom-regexp-string-match.js (0 => 227107)
--- trunk/JSTests/stress/phantom-regexp-string-match.js (rev 0)
+++ trunk/JSTests/stress/phantom-regexp-string-match.js 2018-01-18 04:17:32 UTC (rev 227107)
@@ -0,0 +1,26 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test(string, flag)
+{
+ var regexp = /oa/;
+ var result = string.match(regexp);
+ if (flag)
+ return regexp;
+ return result;
+}
+noInline(test);
+
+for (var i = 0; i < 1e5; ++i) {
+ if (i & 1) {
+ var result = test("Cocoa", true);
+ shouldBe(result instanceof RegExp, true);
+ } else {
+ var result = test("Cocoa", false);
+ shouldBe(result.input, "Cocoa");
+ shouldBe(result[0], "oa");
+ }
+}
Added: trunk/JSTests/stress/regexp-last-index-sinking.js (0 => 227107)
--- trunk/JSTests/stress/regexp-last-index-sinking.js (rev 0)
+++ trunk/JSTests/stress/regexp-last-index-sinking.js 2018-01-18 04:17:32 UTC (rev 227107)
@@ -0,0 +1,23 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test(num)
+{
+ var regexp = /regexp/;
+ if (num & 1)
+ regexp.lastIndex = 42;
+ else
+ regexp.lastIndex = 2;
+ return regexp.lastIndex;
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i) {
+ if (i & 1)
+ shouldBe(test(i), 42);
+ else
+ shouldBe(test(i), 2);
+}
Modified: trunk/Source/_javascript_Core/ChangeLog (227106 => 227107)
--- trunk/Source/_javascript_Core/ChangeLog 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/ChangeLog 2018-01-18 04:17:32 UTC (rev 227107)
@@ -1,5 +1,106 @@
2018-01-17 Yusuke Suzuki <utatane....@gmail.com>
+ [DFG][FTL] Introduce PhantomNewRegexp and RegExpExecNonGlobalOrSticky
+ https://bugs.webkit.org/show_bug.cgi?id=181535
+
+ Reviewed by Saam Barati.
+
+ When executing the code like `string.match(/regexp/)`, `/regexp/` object is created every time we execute this code.
+ However, user rarely cares about this `/regexp/` object. Typically, it is soon discarded even if it has `lastIndex`
+ information. So we should not create RegExpObject for this typical case.
+
+ This patch introduces PhantomNewRegexp. We convert NewRegexp node to PhantomNewRegexp in Object Allocation Sinking (OAS)
+ phase. We should do this analysis in OAS phase since we track modifications to `lastIndex` in the OAS phase. Even if
+ `lastIndex` is modified, it may not be read by users. So we have a chance to drop this NewRegexp beacause we carefully model
+ SetRegExpObjectLastIndex and GetRegExpObjectLastIndex in OAS phase.
+
+ This patch is a first attempt to drop NewRegexp. So we start optimizing it with the simple step: we first drop RegExp with
+ non-global and non-sticky one. We can later extend this optimization for RegExp with global flag. But this is not included
+ in this patch.
+
+ We convert RegExpExec to RegExpExecNonGlobalOrSticky if we find that the given RegExpObject's RegExp is not global/sticky
+ flagged. Since we do not need to touch `lastIndex` property in this case, RegExpExecNonGlobalOrSticky just takes RegExp
+ instead of RegExpObject. This offers the chance to make NewRegExp unused.
+
+ We also convert RegExpMatchFast to RegExpExecNonGlobalOrSticky if its RegExpObject's RegExp is non-global and non-sticky,
+ since they are the same behavior.
+
+ The above optimization completely removes NewRegexp in SixSpeed's regexp-u.{es5,es6}. The resulted execution time is
+ somewhat pure execution time of our Yarr implementation.
+
+ baseline patched
+
+ regex-u.es5 34.8557+-0.5963 ^ 6.1507+-0.5526 ^ definitely 5.6670x faster
+ regex-u.es6 89.1919+-3.3851 ^ 32.0917+-0.4260 ^ definitely 2.7793x faster
+
+ This patch does not change Octane/RegExp so much since it heavily uses String.prototype.replace, which is not handled in
+ this patch right now. We should support StringReplace node in subsequent patches.
+
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGClobbersExitState.cpp:
+ (JSC::DFG::clobbersExitState):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::Graph::dump):
+ * dfg/DFGMayExit.cpp:
+ * dfg/DFGNode.cpp:
+ (JSC::DFG::Node::convertToRegExpExecNonGlobalOrSticky):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::convertToPhantomNewRegexp):
+ (JSC::DFG::Node::convertToSetRegExpObjectLastIndex):
+ (JSC::DFG::Node::hasHeapPrediction):
+ (JSC::DFG::Node::hasCellOperand):
+ (JSC::DFG::Node::isPhantomAllocation):
+ (JSC::DFG::Node::hasIgnoreLastIndexIsWritable):
+ (JSC::DFG::Node::ignoreLastIndexIsWritable):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGObjectAllocationSinkingPhase.cpp:
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGPromotedHeapLocation.cpp:
+ (WTF::printInternal):
+ * dfg/DFGPromotedHeapLocation.h:
+ (JSC::DFG::PromotedLocationDescriptor::neededForMaterialization const):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileNewRegexp):
+ (JSC::DFG::SpeculativeJIT::compileSetRegExpObjectLastIndex):
+ (JSC::DFG::SpeculativeJIT::compileRegExpExecNonGlobalOrSticky):
+ * dfg/DFGSpeculativeJIT.h:
+ (JSC::DFG::SpeculativeJIT::callOperation):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGStrengthReductionPhase.cpp:
+ (JSC::DFG::StrengthReductionPhase::handleNode):
+ * dfg/DFGValidate.cpp:
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileRegExpExecNonGlobalOrSticky):
+ (JSC::FTL::DFG::LowerDFGToB3::compileNewRegexp):
+ (JSC::FTL::DFG::LowerDFGToB3::compileSetRegExpObjectLastIndex):
+ * ftl/FTLOperations.cpp:
+ (JSC::FTL::operationPopulateObjectInOSR):
+ (JSC::FTL::operationMaterializeObjectInOSR):
+ * jit/JITOperations.h:
+ * runtime/RegExpObject.h:
+ (JSC::RegExpObject::create):
+
+2018-01-17 Yusuke Suzuki <utatane....@gmail.com>
+
[FTL] Remove unused helper functions to convert node to PutHint
https://bugs.webkit.org/show_bug.cgi?id=181775
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2018-01-18 04:17:32 UTC (rev 227107)
@@ -1935,11 +1935,15 @@
}
case RegExpExec:
- if (node->child2().useKind() == RegExpObjectUse
- && node->child3().useKind() == StringUse) {
- // This doesn't clobber the world since there are no conversions to perform.
- } else
- clobberWorld(node->origin.semantic, clobberLimit);
+ case RegExpExecNonGlobalOrSticky:
+ if (node->op() == RegExpExec) {
+ if (node->child2().useKind() == RegExpObjectUse
+ && node->child3().useKind() == StringUse) {
+ // This doesn't clobber the world since there are no conversions to perform.
+ } else
+ clobberWorld(node->origin.semantic, clobberLimit);
+ }
+
if (JSValue globalObjectValue = forNode(node->child1()).m_value) {
if (JSGlobalObject* globalObject = jsDynamicCast<JSGlobalObject*>(m_vm, globalObjectValue)) {
if (!globalObject->isHavingABadTime()) {
@@ -2254,6 +2258,7 @@
case PhantomSpread:
case PhantomNewArrayWithSpread:
case PhantomNewArrayBuffer:
+ case PhantomNewRegexp:
case BottomValue:
m_state.setDidClobber(true); // Prevent constant folding.
// This claims to return bottom.
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -4499,7 +4499,7 @@
case op_new_regexp: {
RegExp* regexp = m_inlineStackTop->m_codeBlock->regexp(currentInstruction[2].u.operand);
FrozenValue* frozen = m_graph.freezeStrong(regexp);
- set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(NewRegexp, OpInfo(frozen)));
+ set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(NewRegexp, OpInfo(frozen), jsConstant(jsNumber(0))));
NEXT_OPCODE(op_new_regexp);
}
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2018-01-18 04:17:32 UTC (rev 227107)
@@ -1475,6 +1475,7 @@
case PhantomNewAsyncGeneratorFunction:
case PhantomCreateActivation:
case MaterializeCreateActivation:
+ case PhantomNewRegexp:
read(HeapObjectCount);
write(HeapObjectCount);
return;
@@ -1504,6 +1505,11 @@
write(Heap);
return;
+ case RegExpExecNonGlobalOrSticky:
+ read(RegExpState);
+ write(RegExpState);
+ return;
+
case StringReplace:
case StringReplaceRegExp:
if (node->child1().useKind() == StringUse
Modified: trunk/Source/_javascript_Core/dfg/DFGClobbersExitState.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGClobbersExitState.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGClobbersExitState.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -67,6 +67,7 @@
case PhantomNewAsyncFunction:
case PhantomCreateActivation:
case MaterializeCreateActivation:
+ case PhantomNewRegexp:
case CountExecution:
case SuperSamplerBegin:
case SuperSamplerEnd:
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -137,6 +137,7 @@
case AssertNotEmpty:
case CheckStringIdent:
case RegExpExec:
+ case RegExpExecNonGlobalOrSticky:
case RegExpTest:
case RegExpMatchFast:
case CompareLess:
@@ -272,6 +273,7 @@
case PhantomNewArrayBuffer:
case PhantomSpread:
case PhantomClonedArguments:
+ case PhantomNewRegexp:
case GetMyArgumentByVal:
case GetMyArgumentByValOutOfBounds:
case ForwardVarargs:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -1443,7 +1443,7 @@
if (uid == vm().propertyNames->lastIndex.impl()
&& node->child1()->shouldSpeculateRegExpObject()) {
- node->setOp(SetRegExpObjectLastIndex);
+ node->convertToSetRegExpObjectLastIndex();
fixEdge<RegExpObjectUse>(node->child1());
speculateForBarrier(node->child2());
break;
@@ -1651,6 +1651,7 @@
case PhantomNewArrayWithSpread:
case PhantomNewArrayBuffer:
case PhantomClonedArguments:
+ case PhantomNewRegexp:
case GetMyArgumentByVal:
case GetMyArgumentByValOutOfBounds:
case GetVectorLength:
@@ -1667,6 +1668,7 @@
case GetRegExpObjectLastIndex:
case SetRegExpObjectLastIndex:
case RecordRegExpCachedResult:
+ case RegExpExecNonGlobalOrSticky:
// 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/DFGGraph.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGGraph.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -346,6 +346,8 @@
out.print(comma, "id", data->identifierNumber, "{", identifiers()[data->identifierNumber], "}");
out.print(", domJIT = ", RawPointer(data->domJIT));
}
+ if (node->hasIgnoreLastIndexIsWritable())
+ out.print(comma, "ignoreLastIndexIsWritable = ", node->ignoreLastIndexIsWritable());
if (node->isConstant())
out.print(comma, pointerDumpInContext(node->constant(), context));
if (node->isJump())
Modified: trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -118,9 +118,15 @@
case NewStringObject:
case NewRegexp:
case ToNumber:
+ case RegExpExecNonGlobalOrSticky:
result = ExitsForExceptions;
break;
+ case SetRegExpObjectLastIndex:
+ if (node->ignoreLastIndexIsWritable())
+ break;
+ return Exits;
+
default:
// If in doubt, return true.
return Exits;
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGNode.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -209,6 +209,16 @@
clearFlags(NodeMustGenerate);
}
+void Node::convertToRegExpExecNonGlobalOrSticky(FrozenValue* regExp)
+{
+ ASSERT(op() == RegExpExec);
+ setOpAndDefaultFlags(RegExpExecNonGlobalOrSticky);
+ 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 (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2018-01-18 04:17:32 UTC (rev 227107)
@@ -648,6 +648,15 @@
children = AdjacencyList();
}
+ void convertToPhantomNewRegexp()
+ {
+ ASSERT(m_op == NewRegexp);
+ setOpAndDefaultFlags(PhantomNewRegexp);
+ m_opInfo = OpInfoWrapper();
+ m_opInfo2 = OpInfoWrapper();
+ children = AdjacencyList();
+ }
+
void convertPhantomToPhantomLocal()
{
ASSERT(m_op == Phantom && (child1()->op() == Phi || child1()->op() == SetLocal || child1()->op() == SetArgument));
@@ -727,6 +736,14 @@
void convertToDirectCall(FrozenValue*);
void convertToCallDOM(Graph&);
+
+ void convertToRegExpExecNonGlobalOrSticky(FrozenValue* regExp);
+
+ void convertToSetRegExpObjectLastIndex()
+ {
+ setOp(SetRegExpObjectLastIndex);
+ m_opInfo = false;
+ }
JSValue asJSValue()
{
@@ -1563,6 +1580,7 @@
case ArrayPop:
case ArrayPush:
case RegExpExec:
+ case RegExpExecNonGlobalOrSticky:
case RegExpTest:
case RegExpMatchFast:
case GetGlobalVar:
@@ -1645,6 +1663,7 @@
case DirectTailCall:
case DirectConstruct:
case DirectTailCallInlinedCaller:
+ case RegExpExecNonGlobalOrSticky:
return true;
default:
return false;
@@ -1909,6 +1928,7 @@
case PhantomNewAsyncFunction:
case PhantomNewAsyncGeneratorFunction:
case PhantomCreateActivation:
+ case PhantomNewRegexp:
return true;
default:
return false;
@@ -2654,6 +2674,17 @@
return m_opInfo.as<int32_t>();
}
+ bool hasIgnoreLastIndexIsWritable()
+ {
+ return op() == SetRegExpObjectLastIndex;
+ }
+
+ bool ignoreLastIndexIsWritable()
+ {
+ ASSERT(hasIgnoreLastIndexIsWritable());
+ return m_opInfo.as<uint32_t>();
+ }
+
uint32_t errorType()
{
ASSERT(op() == ThrowStaticError);
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2018-01-18 04:17:32 UTC (rev 227107)
@@ -268,6 +268,7 @@
\
/* Optimizations for regular _expression_ matching. */\
macro(RegExpExec, NodeResultJS | NodeMustGenerate) \
+ macro(RegExpExecNonGlobalOrSticky, NodeResultJS) \
macro(RegExpTest, NodeResultJS | NodeMustGenerate) \
macro(RegExpMatchFast, NodeResultJS | NodeMustGenerate) \
macro(StringReplace, NodeResultJS | NodeMustGenerate) \
@@ -332,6 +333,7 @@
macro(PhantomNewAsyncGeneratorFunction, NodeResultJS | NodeMustGenerate) \
macro(PhantomCreateActivation, NodeResultJS | NodeMustGenerate) \
macro(MaterializeCreateActivation, NodeResultJS | NodeHasVarArgs) \
+ macro(PhantomNewRegexp, NodeResultJS | NodeMustGenerate) \
\
/* Nodes for misc operations. */\
macro(OverridesHasInstance, NodeMustGenerate | NodeResultBoolean) \
Modified: trunk/Source/_javascript_Core/dfg/DFGObjectAllocationSinkingPhase.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGObjectAllocationSinkingPhase.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGObjectAllocationSinkingPhase.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -141,7 +141,7 @@
// once it is escaped if it still has pointers to it in order to
// replace any use of those pointers by the corresponding
// materialization
- enum class Kind { Escaped, Object, Activation, Function, GeneratorFunction, AsyncFunction, AsyncGeneratorFunction };
+ enum class Kind { Escaped, Object, Activation, Function, GeneratorFunction, AsyncFunction, AsyncGeneratorFunction, RegExpObject };
using Fields = HashMap<PromotedLocationDescriptor, Node*>;
@@ -246,6 +246,11 @@
return m_kind == Kind::Function || m_kind == Kind::GeneratorFunction || m_kind == Kind::AsyncFunction;
}
+ bool isRegExpObjectAllocation() const
+ {
+ return m_kind == Kind::RegExpObject;
+ }
+
bool operator==(const Allocation& other) const
{
return m_identifier == other.m_identifier
@@ -290,9 +295,14 @@
case Kind::AsyncGeneratorFunction:
out.print("AsyncGeneratorFunction");
break;
+
case Kind::Activation:
out.print("Activation");
break;
+
+ case Kind::RegExpObject:
+ out.print("RegExpObject");
+ break;
}
out.print("Allocation(");
if (!m_structures.isEmpty())
@@ -849,6 +859,14 @@
break;
}
+ case NewRegexp: {
+ target = &m_heap.newAllocation(node, Allocation::Kind::RegExpObject);
+
+ writes.add(RegExpObjectRegExpPLoc, LazyNode(node->cellOperand()));
+ writes.add(RegExpObjectLastIndexPLoc, LazyNode(node->child1().node()));
+ break;
+ }
+
case CreateActivation: {
if (isStillValid(node->castOperand<SymbolTable*>()->singletonScope())) {
m_heap.escape(node->child1().node());
@@ -1032,6 +1050,26 @@
m_heap.escape(node->child1().node());
break;
+ case GetRegExpObjectLastIndex:
+ target = m_heap.onlyLocalAllocation(node->child1().node());
+ if (target && target->isRegExpObjectAllocation())
+ exactRead = RegExpObjectLastIndexPLoc;
+ else
+ m_heap.escape(node->child1().node());
+ break;
+
+ case SetRegExpObjectLastIndex:
+ target = m_heap.onlyLocalAllocation(node->child1().node());
+ if (target && target->isRegExpObjectAllocation()) {
+ writes.add(
+ PromotedLocationDescriptor(RegExpObjectLastIndexPLoc),
+ LazyNode(node->child2().node()));
+ } else {
+ m_heap.escape(node->child1().node());
+ m_heap.escape(node->child2().node());
+ }
+ break;
+
case Check:
m_graph.doToChildren(
node,
@@ -1508,6 +1546,16 @@
OpInfo(symbolTable), OpInfo(data), 0, 0);
}
+ case Allocation::Kind::RegExpObject: {
+ FrozenValue* regExp = allocation.identifier()->cellOperand();
+ return m_graph.addNode(
+ allocation.identifier()->prediction(), NewRegexp,
+ where->origin.withSemantic(
+ allocation.identifier()->origin.semantic),
+ OpInfo(regExp));
+ break;
+ }
+
default:
DFG_CRASH(m_graph, allocation.identifier(), "Bad allocation kind");
}
@@ -1864,9 +1912,11 @@
case NewGeneratorFunction:
node->convertToPhantomNewGeneratorFunction();
break;
+
case NewAsyncGeneratorFunction:
node->convertToPhantomNewAsyncGeneratorFunction();
break;
+
case NewAsyncFunction:
node->convertToPhantomNewAsyncFunction();
break;
@@ -1875,6 +1925,10 @@
node->convertToPhantomCreateActivation();
break;
+ case NewRegexp:
+ node->convertToPhantomNewRegexp();
+ break;
+
default:
node->remove();
break;
@@ -2143,6 +2197,23 @@
break;
}
+ case NewRegexp: {
+ Vector<PromotedHeapLocation> locations = m_locationsForAllocation.get(escapee);
+ ASSERT(locations.size() == 2);
+
+ PromotedHeapLocation regExp(RegExpObjectRegExpPLoc, allocation.identifier());
+ ASSERT_UNUSED(regExp, locations.contains(regExp));
+
+ PromotedHeapLocation lastIndex(RegExpObjectLastIndexPLoc, allocation.identifier());
+ ASSERT(locations.contains(lastIndex));
+ Node* value = resolve(block, lastIndex);
+ if (m_sinkCandidates.contains(value))
+ node->child1() = Edge(m_bottom);
+ else
+ node->child1() = Edge(value);
+ break;
+ }
+
default:
DFG_CRASH(m_graph, node, "Bad materialize op");
}
@@ -2248,6 +2319,16 @@
break;
}
+ case RegExpObjectLastIndexPLoc: {
+ return m_graph.addNode(
+ SetRegExpObjectLastIndex,
+ origin.takeValidExit(canExit),
+ OpInfo(true),
+ Edge(base, KnownCellUse),
+ value->defaultEdge());
+ break;
+ }
+
default:
DFG_CRASH(m_graph, base, "Bad location kind");
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -60,6 +60,8 @@
#include "ObjectConstructor.h"
#include "Operations.h"
#include "ParseInt.h"
+#include "RegExpConstructor.h"
+#include "RegExpMatchesArray.h"
#include "RegExpObject.h"
#include "Repatch.h"
#include "ScopedArguments.h"
@@ -1023,6 +1025,32 @@
return JSValue::encode(asRegExpObject(base)->exec(exec, globalObject, input));
}
+EncodedJSValue JIT_OPERATION operationRegExpExecNonGlobalOrSticky(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);
+
+ RegExpConstructor* regExpConstructor = globalObject->regExpConstructor();
+ String input = string->value(exec);
+ RETURN_IF_EXCEPTION(scope, { });
+
+ unsigned lastIndex = 0;
+ MatchResult result;
+ JSArray* array = createRegExpMatchesArray(vm, globalObject, string, input, regExp, lastIndex, result);
+ if (!array) {
+ ASSERT(!scope.exception());
+ return JSValue::encode(jsNull());
+ }
+
+ RETURN_IF_EXCEPTION(scope, { });
+ regExpConstructor->recordMatch(vm, regExp, string, result);
+ return JSValue::encode(array);
+}
+
EncodedJSValue JIT_OPERATION operationRegExpMatchFastString(ExecState* exec, JSGlobalObject* globalObject, RegExpObject* regExpObject, JSString* argument)
{
SuperSamplerScope superSamplerScope(false);
@@ -1876,6 +1904,16 @@
return jsString(exec, Identifier::from(exec, index).string());
}
+JSCell* JIT_OPERATION operationNewRegexpWithLastIndex(ExecState* exec, JSCell* regexpPtr, EncodedJSValue encodedLastIndex)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ RegExp* regexp = static_cast<RegExp*>(regexpPtr);
+ ASSERT(regexp->isValid());
+ return RegExpObject::create(vm, exec->lexicalGlobalObject()->regExpStructure(), regexp, JSValue::decode(encodedLastIndex));
+}
+
StringImpl* JIT_OPERATION operationResolveRope(ExecState* exec, JSString* string)
{
VM& vm = exec->vm();
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.h 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h 2018-01-18 04:17:32 UTC (rev 227107)
@@ -85,6 +85,7 @@
JSCell* JIT_OPERATION operationGetPropertyEnumerator(ExecState*, EncodedJSValue);
JSCell* JIT_OPERATION operationGetPropertyEnumeratorCell(ExecState*, JSCell*);
JSCell* JIT_OPERATION operationToIndexString(ExecState*, int32_t);
+JSCell* JIT_OPERATION operationNewRegexpWithLastIndex(ExecState*, JSCell*, EncodedJSValue) WTF_INTERNAL;
char* JIT_OPERATION operationNewArray(ExecState*, Structure*, void*, size_t) WTF_INTERNAL;
char* JIT_OPERATION operationNewEmptyArray(ExecState*, Structure*) WTF_INTERNAL;
char* JIT_OPERATION operationNewArrayWithSize(ExecState*, Structure*, int32_t, Butterfly*) WTF_INTERNAL;
@@ -151,6 +152,7 @@
EncodedJSValue JIT_OPERATION operationRegExpExecString(ExecState*, JSGlobalObject*, RegExpObject*, JSString*) WTF_INTERNAL;
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 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 (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -696,6 +696,7 @@
case ArrayPop:
case ArrayPush:
case RegExpExec:
+ case RegExpExecNonGlobalOrSticky:
case RegExpTest:
case RegExpMatchFast:
case StringReplace:
@@ -1091,6 +1092,7 @@
case PhantomNewArrayWithSpread:
case PhantomNewArrayBuffer:
case PhantomClonedArguments:
+ case PhantomNewRegexp:
case GetMyArgumentByVal:
case GetMyArgumentByValOutOfBounds:
case PutHint:
Modified: trunk/Source/_javascript_Core/dfg/DFGPromotedHeapLocation.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGPromotedHeapLocation.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGPromotedHeapLocation.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -126,6 +126,14 @@
case NewArrayBufferPLoc:
out.print("NewArrayBufferPLoc");
return;
+
+ case RegExpObjectRegExpPLoc:
+ out.print("RegExpObjectRegExpPLoc");
+ return;
+
+ case RegExpObjectLastIndexPLoc:
+ out.print("RegExpObjectLastIndexPLoc");
+ return;
}
RELEASE_ASSERT_NOT_REACHED();
Modified: trunk/Source/_javascript_Core/dfg/DFGPromotedHeapLocation.h (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGPromotedHeapLocation.h 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGPromotedHeapLocation.h 2018-01-18 04:17:32 UTC (rev 227107)
@@ -64,6 +64,8 @@
SpreadPLoc,
NewArrayWithSpreadArgumentPLoc,
NewArrayBufferPLoc,
+ RegExpObjectRegExpPLoc,
+ RegExpObjectLastIndexPLoc,
};
class PromotedLocationDescriptor {
@@ -117,6 +119,7 @@
switch (kind()) {
case NamedPropertyPLoc:
case ClosureVarPLoc:
+ case RegExpObjectLastIndexPLoc:
return false;
default:
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2018-01-18 04:17:32 UTC (rev 227107)
@@ -261,6 +261,7 @@
case AssertNotEmpty:
case CheckStringIdent:
case RegExpExec:
+ case RegExpExecNonGlobalOrSticky:
case RegExpTest:
case RegExpMatchFast:
case CompareLess:
@@ -393,6 +394,7 @@
case PhantomNewAsyncGeneratorFunction:
case PhantomNewAsyncFunction:
case PhantomCreateActivation:
+ case PhantomNewRegexp:
case PutHint:
case CheckStructureImmediate:
case MaterializeNewObject:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -9075,10 +9075,12 @@
GPRTemporary result(this);
GPRTemporary scratch1(this);
GPRTemporary scratch2(this);
+ JSValueOperand lastIndex(this, node->child1());
GPRReg resultGPR = result.gpr();
GPRReg scratch1GPR = scratch1.gpr();
GPRReg scratch2GPR = scratch2.gpr();
+ JSValueRegs lastIndexRegs = lastIndex.jsValueRegs();
JITCompiler::JumpList slowPath;
@@ -9090,11 +9092,11 @@
m_jit.storePtr(
TrustedImmPtr(node->cellOperand()),
CCallHelpers::Address(resultGPR, RegExpObject::offsetOfRegExp()));
- m_jit.storeTrustedValue(jsNumber(0), CCallHelpers::Address(resultGPR, RegExpObject::offsetOfLastIndex()));
+ m_jit.storeValue(lastIndexRegs, CCallHelpers::Address(resultGPR, RegExpObject::offsetOfLastIndex()));
m_jit.store8(TrustedImm32(true), CCallHelpers::Address(resultGPR, RegExpObject::offsetOfLastIndexIsWritable()));
m_jit.mutatorFence(*m_jit.vm());
- addSlowPathGenerator(slowPathCall(slowPath, this, operationNewRegexp, resultGPR, regexp));
+ addSlowPathGenerator(slowPathCall(slowPath, this, operationNewRegexpWithLastIndex, resultGPR, regexp, lastIndexRegs));
cellResult(resultGPR, node);
}
@@ -10421,12 +10423,16 @@
JSValueOperand value(this, node->child2());
GPRReg regExpGPR = regExp.gpr();
JSValueRegs valueRegs = value.jsValueRegs();
- speculateRegExpObject(node->child1(), regExpGPR);
- speculationCheck(
- ExoticObjectMode, JSValueRegs(), nullptr,
- m_jit.branchTest8(
- JITCompiler::Zero,
- JITCompiler::Address(regExpGPR, RegExpObject::offsetOfLastIndexIsWritable())));
+
+ if (!node->ignoreLastIndexIsWritable()) {
+ speculateRegExpObject(node->child1(), regExpGPR);
+ speculationCheck(
+ ExoticObjectMode, JSValueRegs(), nullptr,
+ m_jit.branchTest8(
+ JITCompiler::Zero,
+ JITCompiler::Address(regExpGPR, RegExpObject::offsetOfLastIndexIsWritable())));
+ }
+
m_jit.storeValue(valueRegs, JITCompiler::Address(regExpGPR, RegExpObject::offsetOfLastIndex()));
noResult(node);
}
@@ -10620,6 +10626,26 @@
m_jit.decrementSuperSamplerCount();
}
+void SpeculativeJIT::compileRegExpExecNonGlobalOrSticky(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(
+ operationRegExpExecNonGlobalOrSticky, 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 (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2018-01-18 04:17:32 UTC (rev 227107)
@@ -1431,6 +1431,11 @@
m_jit.setupArgumentsWithExecState(arg1, arg2.gpr());
return appendCallSetResult(operation, result);
}
+ JITCompiler::Call callOperation(C_JITOperation_ECJ operation, GPRReg result, JSCell* arg1, JSValueRegs arg2)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr::weakPointer(m_jit.graph(), arg1), arg2.gpr());
+ return appendCallSetResult(operation, result);
+ }
JITCompiler::Call callOperation(C_JITOperation_ECO operation, GPRReg result, GPRReg arg1, GPRReg arg2)
{
m_jit.setupArgumentsWithExecState(arg1, arg2);
@@ -1578,6 +1583,11 @@
m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
return appendCallSetResult(operation, result.payloadGPR());
}
+ JITCompiler::Call callOperation(J_JITOperation_EGReJss operation, JSValueRegs result, GPRReg arg1, TrustedImmPtr arg2, GPRReg arg3)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
+ return appendCallSetResult(operation, result.payloadGPR());
+ }
JITCompiler::Call callOperation(C_JITOperation_EJssReo operation, GPRReg result, GPRReg arg1, GPRReg arg2)
{
m_jit.setupArgumentsWithExecState(arg1, arg2);
@@ -2086,6 +2096,11 @@
m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR(), arg2.tagGPR());
return appendCallSetResult(operation, result);
}
+ JITCompiler::Call callOperation(C_JITOperation_ECJ operation, GPRReg result, JSCell* arg1, JSValueRegs arg2)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr::weakPointer(m_jit.graph(), arg1), arg2.payloadGPR(), arg2.tagGPR());
+ return appendCallSetResult(operation, result);
+ }
JITCompiler::Call callOperation(C_JITOperation_ECO operation, GPRReg result, GPRReg arg1, GPRReg arg2)
{
m_jit.setupArgumentsWithExecState(arg1, arg2);
@@ -2321,6 +2336,11 @@
m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
return appendCallSetResult(operation, result.payloadGPR(), result.tagGPR());
}
+ JITCompiler::Call callOperation(J_JITOperation_EGReJss operation, JSValueRegs result, GPRReg arg1, TrustedImmPtr arg2, GPRReg arg3)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
+ return appendCallSetResult(operation, result.payloadGPR(), result.tagGPR());
+ }
JITCompiler::Call callOperation(J_JITOperation_ESsiCI operation, JSValueRegs result, StructureStubInfo* stubInfo, GPRReg arg1, const UniquedStringImpl* uid)
{
m_jit.setupArgumentsWithExecState(TrustedImmPtr(stubInfo), arg1, TrustedImmPtr(uid));
@@ -3065,6 +3085,7 @@
void compileArrayPush(Node*);
void compileNotifyWrite(Node*);
void compileRegExpExec(Node*);
+ void compileRegExpExecNonGlobalOrSticky(Node*);
void compileRegExpMatchFast(Node*);
void compileRegExpTest(Node*);
void compileStringReplace(Node*);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -3162,6 +3162,11 @@
compileRegExpExec(node);
break;
}
+
+ case RegExpExecNonGlobalOrSticky: {
+ compileRegExpExecNonGlobalOrSticky(node);
+ break;
+ }
case RegExpTest: {
compileRegExpTest(node);
@@ -5159,6 +5164,7 @@
case PhantomNewAsyncFunction:
case PhantomNewAsyncGeneratorFunction:
case PhantomCreateActivation:
+ case PhantomNewRegexp:
case PutHint:
case CheckStructureImmediate:
case MaterializeCreateActivation:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -3433,6 +3433,11 @@
break;
}
+ case RegExpExecNonGlobalOrSticky: {
+ compileRegExpExecNonGlobalOrSticky(node);
+ break;
+ }
+
case RegExpTest: {
compileRegExpTest(node);
break;
@@ -5741,6 +5746,7 @@
case PhantomNewAsyncFunction:
case PhantomNewAsyncGeneratorFunction:
case PhantomCreateActivation:
+ case PhantomNewRegexp:
case GetMyArgumentByVal:
case GetMyArgumentByValOutOfBounds:
case GetVectorLength:
Modified: trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -462,7 +462,9 @@
}
case RegExpExec:
- case RegExpTest: {
+ case RegExpTest:
+ case RegExpMatchFast:
+ case RegExpExecNonGlobalOrSticky: {
JSGlobalObject* globalObject = m_node->child1()->dynamicCastConstant<JSGlobalObject*>(vm());
if (!globalObject) {
if (verbose)
@@ -476,250 +478,297 @@
break;
}
- Node* regExpObjectNode = m_node->child2().node();
- RegExp* regExp;
- if (RegExpObject* regExpObject = regExpObjectNode->dynamicCastConstant<RegExpObject*>(vm()))
- regExp = regExpObject->regExp();
- else if (regExpObjectNode->op() == NewRegexp)
- regExp = regExpObjectNode->castOperand<RegExp*>();
- else {
- if (verbose)
- dataLog("Giving up because the regexp is unknown.\n");
- break;
- }
+ Node* regExpObjectNode = nullptr;
+ RegExp* regExp = nullptr;
+ if (m_node->op() == RegExpExec || m_node->op() == RegExpTest || m_node->op() == RegExpMatchFast) {
+ regExpObjectNode = m_node->child2().node();
+ if (RegExpObject* regExpObject = regExpObjectNode->dynamicCastConstant<RegExpObject*>(vm()))
+ regExp = regExpObject->regExp();
+ else if (regExpObjectNode->op() == NewRegexp)
+ regExp = regExpObjectNode->castOperand<RegExp*>();
+ else {
+ if (verbose)
+ dataLog("Giving up because the regexp is unknown.\n");
+ break;
+ }
+ } else
+ regExp = m_node->castOperand<RegExp*>();
- 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;
+ if (m_node->op() == RegExpMatchFast) {
+ if (!regExp->global()) {
+ m_node->setOp(RegExpExec);
+ m_changed = true;
+ // Continue performing strength reduction onto RegExpExec node.
+ } else
+ break;
}
- FrozenValue* regExpFrozenValue = m_graph.freeze(regExp);
+ ASSERT(m_node->op() != RegExpMatchFast);
- // 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;
- }
+ auto foldToConstant = [&] {
+ Node* stringNode = nullptr;
+ if (m_node->op() == RegExpExecNonGlobalOrSticky)
+ stringNode = m_node->child2().node();
+ else
+ stringNode = m_node->child3().node();
- if (m_node->op() == RegExpExec && regExp->hasNamedCaptures()) {
- // FIXME: https://bugs.webkit.org/show_bug.cgi?id=176464
- // Implement strength reduction optimization for named capture groups.
- if (verbose)
- dataLog("Giving up because of named capture groups.\n");
- break;
- }
+ // 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 = stringNode->tryGetString(m_graph);
+ if (!string) {
+ if (verbose)
+ dataLog("Giving up because the string is unknown.\n");
+ return false;
+ }
- 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;
+ 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");
+ return false;
}
- if (lastIndex == UINT_MAX) {
+
+ if ((m_node->op() == RegExpExec || m_node->op() == RegExpExecNonGlobalOrSticky) && regExp->hasNamedCaptures()) {
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=176464
+ // Implement strength reduction optimization for named capture groups.
if (verbose)
- dataLog("Giving up because the last index is not known.\n");
- break;
+ dataLog("Giving up because of named capture groups.\n");
+ return false;
}
- } else
- lastIndex = 0;
- m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint());
-
- Structure* structure;
- if (m_node->op() == RegExpExec && regExp->hasNamedCaptures())
- structure = globalObject->regExpMatchesArrayWithGroupsStructure();
- else
- structure = globalObject->regExpMatchesArrayStructure();
+ 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.
+ ASSERT(regExpObjectNode);
+ 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");
+ return false;
+ }
+ } else
+ lastIndex = 0;
- 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);
+ m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint());
- RegExpConstructor* constructor = globalObject->regExpConstructor();
- FrozenValue* constructorFrozenValue = m_graph.freeze(constructor);
+ Structure* structure;
+ if ((m_node->op() == RegExpExec || m_node->op() == RegExpExecNonGlobalOrSticky) && regExp->hasNamedCaptures())
+ structure = globalObject->regExpMatchesArrayWithGroupsStructure();
+ else
+ structure = globalObject->regExpMatchesArrayStructure();
- MatchResult result;
- Vector<int> 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 (structure->indexingType() != ArrayWithContiguous) {
+ // This is further protection against a race with haveABadTime.
if (verbose)
- dataLog("Giving up because match failed.\n");
- break;
+ dataLog("Giving up because the structure has the wrong indexing type.\n");
+ return false;
}
- 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;
+ m_graph.registerStructure(structure);
+
+ RegExpConstructor* constructor = globalObject->regExpConstructor();
+ FrozenValue* constructorFrozenValue = m_graph.freeze(constructor);
+
+ MatchResult result;
+ Vector<int> 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 || m_node->op() == RegExpExecNonGlobalOrSticky) {
+ int position;
+ if (!regExp->matchConcurrently(vm(), string, lastIndex, position, ovector)) {
+ if (verbose)
+ dataLog("Giving up because match failed.\n");
+ return false;
+ }
+ result.start = position;
+ result.end = ovector[1];
+ } else {
+ if (!regExp->matchConcurrently(vm(), string, lastIndex, result)) {
+ if (verbose)
+ dataLog("Giving up because match failed.\n");
+ return false;
+ }
}
- }
- // We've constant-folded the regexp. Now we're committed to replacing RegExpExec/Test.
+ // We've constant-folded the regexp. Now we're committed to replacing RegExpExec/Test.
- m_changed = true;
+ m_changed = true;
- NodeOrigin origin = m_node->origin;
+ NodeOrigin origin = m_node->origin;
- m_insertionSet.insertNode(
- m_nodeIndex, SpecNone, Check, origin, m_node->children.justChecks());
+ m_insertionSet.insertNode(
+ m_nodeIndex, SpecNone, Check, origin, m_node->children.justChecks());
- if (m_node->op() == RegExpExec) {
- if (result) {
- RegisteredStructureSet* structureSet = m_graph.addStructureSet(structure);
+ if (m_node->op() == RegExpExec || m_node->op() == RegExpExecNonGlobalOrSticky) {
+ if (result) {
+ RegisteredStructureSet* 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());
- }
+ // 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 =
- Butterfly::optimalContiguousVectorLength(structure, publicLength);
+ unsigned publicLength = resultArray.size();
+ unsigned vectorLength =
+ Butterfly::optimalContiguousVectorLength(structure, 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);
+ 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_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()) {
+ ASSERT(regExpObjectNode);
+ m_insertionSet.insertNode(
+ m_nodeIndex, SpecNone, SetRegExpObjectLastIndex, origin,
+ OpInfo(false),
+ 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, structure, KnownCellUse));
- ObjectMaterializationData* data = ""
-
+ m_nodeIndex, origin, constructorFrozenValue, KnownCellUse));
m_graph.m_varArgChildren.append(
m_insertionSet.insertConstantForUse(
- m_nodeIndex, origin, jsNumber(publicLength), KnownInt32Use));
- data->m_properties.append(PublicLengthPLoc);
-
+ 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(vectorLength), KnownInt32Use));
- data->m_properties.append(VectorLengthPLoc);
-
+ m_nodeIndex, origin, jsNumber(result.start), KnownInt32Use));
m_graph.m_varArgChildren.append(
m_insertionSet.insertConstantForUse(
- m_nodeIndex, origin, jsNumber(result.start), UntypedUse));
- data->m_properties.append(
- PromotedLocationDescriptor(NamedPropertyPLoc, indexIndex));
+ 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);
- m_graph.m_varArgChildren.append(Edge(stringNode, UntypedUse));
- data->m_properties.append(
- PromotedLocationDescriptor(NamedPropertyPLoc, inputIndex));
+ origin = origin.withInvalidExit();
+ }
- 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)));
- };
+ m_node->origin = origin;
+ return true;
+ };
- 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()) {
+ auto convertToStatic = [&] {
+ if (m_node->op() != RegExpExec)
+ return false;
+ if (regExp->globalOrSticky())
+ return false;
+ if (m_node->child3().useKind() != StringUse)
+ return false;
+ NodeOrigin origin = m_node->origin;
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();
- }
+ m_nodeIndex, SpecNone, Check, origin, m_node->children.justChecks());
+ m_node->convertToRegExpExecNonGlobalOrSticky(m_graph.freeze(regExp));
+ m_changed = true;
+ return true;
+ };
- 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);
+ if (foldToConstant())
+ break;
- origin = origin.withInvalidExit();
- }
-
- m_node->origin = origin;
+ if (convertToStatic())
+ break;
+
break;
}
@@ -802,6 +851,7 @@
if (regExp->global()) {
m_insertionSet.insertNode(
m_nodeIndex, SpecNone, SetRegExpObjectLastIndex, origin,
+ OpInfo(false),
Edge(regExpObjectNode, RegExpObjectUse),
m_insertionSet.insertConstantForUse(
m_nodeIndex, origin, jsNumber(0), UntypedUse));
Modified: trunk/Source/_javascript_Core/dfg/DFGValidate.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -582,6 +582,7 @@
case PhantomNewAsyncFunction:
case PhantomNewAsyncGeneratorFunction:
case PhantomCreateActivation:
+ case PhantomNewRegexp:
case GetMyArgumentByVal:
case GetMyArgumentByValOutOfBounds:
case PutHint:
@@ -738,6 +739,7 @@
case PhantomDirectArguments:
case PhantomCreateRest:
case PhantomClonedArguments:
+ case PhantomNewRegexp:
case MovHint:
case Upsilon:
case ForwardVarargs:
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -246,6 +246,7 @@
case PhantomNewAsyncGeneratorFunction:
case PhantomNewAsyncFunction:
case PhantomCreateActivation:
+ case PhantomNewRegexp:
case PutHint:
case CheckStructureImmediate:
case MaterializeNewObject:
@@ -276,6 +277,7 @@
case CreateRest:
case GetRestLength:
case RegExpExec:
+ case RegExpExecNonGlobalOrSticky:
case RegExpTest:
case RegExpMatchFast:
case NewRegexp:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -1182,6 +1182,9 @@
case RegExpExec:
compileRegExpExec();
break;
+ case RegExpExecNonGlobalOrSticky:
+ compileRegExpExecNonGlobalOrSticky();
+ break;
case RegExpTest:
compileRegExpTest();
break;
@@ -1267,6 +1270,7 @@
case PhantomNewArrayWithSpread:
case PhantomNewArrayBuffer:
case PhantomClonedArguments:
+ case PhantomNewRegexp:
case PutHint:
case BottomValue:
case KillStack:
@@ -10260,6 +10264,15 @@
setJSValue(result);
}
+ void compileRegExpExecNonGlobalOrSticky()
+ {
+ LValue globalObject = lowCell(m_node->child1());
+ LValue argument = lowString(m_node->child2());
+ LValue result = vmCall(
+ Int64, m_out.operation(operationRegExpExecNonGlobalOrSticky), m_callFrame, globalObject, frozenPointer(m_node->cellOperand()), argument);
+ setJSValue(result);
+ }
+
void compileRegExpTest()
{
LValue globalObject = lowCell(m_node->child1());
@@ -10306,6 +10319,7 @@
void compileNewRegexp()
{
FrozenValue* regexp = m_node->cellOperand();
+ LValue lastIndex = lowJSValue(m_node->child1());
ASSERT(regexp->cell()->inherits(vm(), RegExp::info()));
ASSERT(m_node->castOperand<RegExp*>()->isValid());
@@ -10317,7 +10331,7 @@
auto structure = m_graph.registerStructure(m_graph.globalObjectFor(m_node->origin.semantic)->regExpStructure());
LValue fastResultValue = allocateObject<RegExpObject>(structure, m_out.intPtrZero, m_out.int32Zero, slowCase);
m_out.storePtr(frozenPointer(regexp), fastResultValue, m_heaps.RegExpObject_regExp);
- m_out.store64(m_out.constInt64(JSValue::encode(jsNumber(0))), fastResultValue, m_heaps.RegExpObject_lastIndex);
+ m_out.store64(lastIndex, fastResultValue, m_heaps.RegExpObject_lastIndex);
m_out.store32As8(m_out.constInt32(true), m_out.address(fastResultValue, m_heaps.RegExpObject_lastIndexIsWritable));
mutatorFence();
ValueFromBlock fastResult = m_out.anchor(fastResultValue);
@@ -10329,9 +10343,9 @@
LValue slowResultValue = lazySlowPath(
[=, &vm] (const Vector<Location>& locations) -> RefPtr<LazySlowPath::Generator> {
return createLazyCallGenerator(vm,
- operationNewRegexp, locations[0].directGPR(),
- CCallHelpers::TrustedImmPtr(regexpCell));
- });
+ operationNewRegexpWithLastIndex, locations[0].directGPR(),
+ CCallHelpers::TrustedImmPtr(regexpCell), locations[1].directGPR());
+ }, lastIndex);
ValueFromBlock slowResult = m_out.anchor(slowResultValue);
m_out.jump(continuation);
@@ -10398,14 +10412,19 @@
void compileSetRegExpObjectLastIndex()
{
- LValue regExp = lowRegExpObject(m_node->child1());
- LValue value = lowJSValue(m_node->child2());
+ if (!m_node->ignoreLastIndexIsWritable()) {
+ LValue regExp = lowRegExpObject(m_node->child1());
+ LValue value = lowJSValue(m_node->child2());
- speculate(
- ExoticObjectMode, noValue(), nullptr,
- m_out.isZero32(m_out.load8ZeroExt32(regExp, m_heaps.RegExpObject_lastIndexIsWritable)));
+ speculate(
+ ExoticObjectMode, noValue(), nullptr,
+ m_out.isZero32(m_out.load8ZeroExt32(regExp, m_heaps.RegExpObject_lastIndexIsWritable)));
+
+ m_out.store64(value, regExp, m_heaps.RegExpObject_lastIndex);
+ return;
+ }
- m_out.store64(value, regExp, m_heaps.RegExpObject_lastIndex);
+ m_out.store64(lowJSValue(m_node->child2()), lowCell(m_node->child1()), m_heaps.RegExpObject_lastIndex);
}
void compileLogShadowChickenPrologue()
Modified: trunk/Source/_javascript_Core/ftl/FTLOperations.cpp (227106 => 227107)
--- trunk/Source/_javascript_Core/ftl/FTLOperations.cpp 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/ftl/FTLOperations.cpp 2018-01-18 04:17:32 UTC (rev 227107)
@@ -40,6 +40,7 @@
#include "JSFixedArray.h"
#include "JSGeneratorFunction.h"
#include "JSLexicalEnvironment.h"
+#include "RegExpObject.h"
namespace JSC { namespace FTL {
@@ -110,7 +111,20 @@
break;
}
+ case PhantomNewRegexp: {
+ RegExpObject* regExpObject = jsCast<RegExpObject*>(JSValue::decode(*encodedValue));
+ for (unsigned i = materialization->properties().size(); i--;) {
+ const ExitPropertyValue& property = materialization->properties()[i];
+ if (property.location().kind() != RegExpObjectLastIndexPLoc)
+ continue;
+
+ regExpObject->setLastIndex(exec, JSValue::decode(values[i]), false /* shouldThrow */);
+ break;
+ }
+ break;
+ }
+
default:
RELEASE_ASSERT_NOT_REACHED();
break;
@@ -533,7 +547,21 @@
return result;
}
-
+ case PhantomNewRegexp: {
+ RegExp* regExp = nullptr;
+ for (unsigned i = materialization->properties().size(); i--;) {
+ const ExitPropertyValue& property = materialization->properties()[i];
+ if (property.location() == PromotedLocationDescriptor(RegExpObjectRegExpPLoc)) {
+ RELEASE_ASSERT(JSValue::decode(values[i]).asCell()->inherits(vm, RegExp::info()));
+ regExp = jsCast<RegExp*>(JSValue::decode(values[i]));
+ }
+ }
+ RELEASE_ASSERT(regExp);
+ CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(materialization->origin(), exec->codeBlock());
+ Structure* structure = codeBlock->globalObject()->regExpStructure();
+ return RegExpObject::create(vm, structure, regExp);
+ }
+
default:
RELEASE_ASSERT_NOT_REACHED();
return nullptr;
Modified: trunk/Source/_javascript_Core/jit/JITOperations.h (227106 => 227107)
--- trunk/Source/_javascript_Core/jit/JITOperations.h 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/jit/JITOperations.h 2018-01-18 04:17:32 UTC (rev 227107)
@@ -51,6 +51,7 @@
class JSScope;
class JSString;
class JSValue;
+class RegExp;
class RegExpObject;
class Register;
class Structure;
@@ -103,6 +104,7 @@
Pc: Instruction* i.e. bytecode PC
Q: int64_t
R: Register
+ Re: RegExp*
Reo: RegExpObject*
S: size_t
Sprt: SlowPathReturnType
@@ -136,6 +138,7 @@
typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EGReoJ)(ExecState*, JSGlobalObject*, RegExpObject*, EncodedJSValue);
typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EGReoJss)(ExecState*, JSGlobalObject*, RegExpObject*, JSString*);
typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EGJJ)(ExecState*, JSGlobalObject*, EncodedJSValue, EncodedJSValue);
+typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EGReJss)(ExecState*, JSGlobalObject*, RegExp*, JSString*);
typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EI)(ExecState*, UniquedStringImpl*);
typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJ)(ExecState*, EncodedJSValue);
typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJZ)(ExecState*, EncodedJSValue, int32_t);
Modified: trunk/Source/_javascript_Core/runtime/RegExpObject.h (227106 => 227107)
--- trunk/Source/_javascript_Core/runtime/RegExpObject.h 2018-01-18 03:15:26 UTC (rev 227106)
+++ trunk/Source/_javascript_Core/runtime/RegExpObject.h 2018-01-18 04:17:32 UTC (rev 227107)
@@ -39,6 +39,13 @@
return object;
}
+ static RegExpObject* create(VM& vm, Structure* structure, RegExp* regExp, JSValue lastIndex)
+ {
+ auto* object = create(vm, structure, regExp);
+ object->m_lastIndex.set(vm, object, lastIndex);
+ return object;
+ }
+
void setRegExp(VM& vm, RegExp* r) { m_regExp.set(vm, this, r); }
RegExp* regExp() const { return m_regExp.get(); }