Skip to content

Commit 23bb940

Browse files
committed
[JSC] RegExpExecNonGlobalOrSticky should have NodeMustGenerate to prevent DCE
DFGStrengthReductionPhase converts RegExpExec to RegExpExecNonGlobalOrSticky when the regexp is a constant without the global or sticky flag. This conversion calls setOpAndDefaultFlags(RegExpExecNonGlobalOrSticky) which resets flags to the default, and RegExpExecNonGlobalOrSticky was missing NodeMustGenerate. DFGClobberize.h correctly declares write(RegExpState) for this node (it shares the same case block as RegExpMatchFastGlobal). When the exec result is unused, DCE eliminates the node and RegExp.lastMatch/$1/etc. are left stale from a prior match: function test(s) { /bc/.exec(s); } // result unused /seed/.test("seeded"); test("abcd"); RegExp.lastMatch; // DFG/FTL: "seed" (stale), expected: "bc" This is the same bug pattern as RegExpMatchFastGlobal, which was fixed in 309271@main (https://bugs.webkit.org/show_bug.cgi?id=309953). Both nodes share the same Clobberize case block and the same conversion pattern in DFGNode.cpp (convertToRegExpExecNonGlobalOrStickyWithoutChecks and convertToRegExpMatchFastGlobalWithoutChecks both call setOpAndDefaultFlags). RegExpExec (the source op) already has NodeMustGenerate. Adding it to RegExpExecNonGlobalOrSticky makes DFG/FTL behavior match lower tiers. Test: JSTests/stress/regexp-exec-non-global-or-sticky-dce-lastmatch.js * JSTests/stress/regexp-exec-non-global-or-sticky-dce-lastmatch.js: Added. (shouldBe): (test): (testCapture): (testLeftRight): (testNoMatch): * Source/JavaScriptCore/dfg/DFGNodeType.h:
1 parent 7da058f commit 23bb940

2 files changed

Lines changed: 61 additions & 1 deletion

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
function shouldBe(actual, expected) {
2+
if (actual !== expected)
3+
throw new Error("bad value: " + JSON.stringify(actual) + " expected: " + JSON.stringify(expected));
4+
}
5+
6+
// RegExpExecNonGlobalOrSticky writes to RegExpState (RegExp.lastMatch, $1, etc.)
7+
// but was missing NodeMustGenerate, allowing FTL DCE to eliminate it when
8+
// the result is unused. This is the same bug pattern as
9+
// https://bugs.webkit.org/show_bug.cgi?id=309953 (RegExpMatchFastGlobal).
10+
//
11+
// DFGStrengthReductionPhase converts RegExpExec to RegExpExecNonGlobalOrSticky
12+
// via convertToRegExpExecNonGlobalOrStickyWithoutChecks when the regexp is a
13+
// constant without the global or sticky flag.
14+
15+
function test(s) {
16+
/bc/.exec(s);
17+
}
18+
noInline(test);
19+
20+
for (let i = 0; i < testLoopCount; ++i) {
21+
/seed/.test("seeded");
22+
test("abcd");
23+
shouldBe(RegExp.lastMatch, "bc");
24+
shouldBe(RegExp.input, "abcd");
25+
}
26+
27+
function testCapture(s) {
28+
/(b)(c)/.exec(s);
29+
}
30+
noInline(testCapture);
31+
32+
for (let i = 0; i < testLoopCount; ++i) {
33+
/(s)(e)/.test("seed");
34+
testCapture("abcd");
35+
shouldBe(RegExp.$1, "b");
36+
shouldBe(RegExp.$2, "c");
37+
}
38+
39+
function testLeftRight(s) {
40+
/bc/.exec(s);
41+
}
42+
noInline(testLeftRight);
43+
44+
for (let i = 0; i < testLoopCount; ++i) {
45+
/seed/.test("seeded");
46+
testLeftRight("abcd");
47+
shouldBe(RegExp.leftContext, "a");
48+
shouldBe(RegExp.rightContext, "d");
49+
}
50+
51+
function testNoMatch(s) {
52+
/xyz/.exec(s);
53+
}
54+
noInline(testNoMatch);
55+
56+
for (let i = 0; i < testLoopCount; ++i) {
57+
/seed/.test("seeded");
58+
testNoMatch("abcd");
59+
shouldBe(RegExp.lastMatch, "seed");
60+
}

Source/JavaScriptCore/dfg/DFGNodeType.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ namespace JSC { namespace DFG {
349349
\
350350
/* Optimizations for regular expression matching. */\
351351
macro(RegExpExec, NodeResultJS | NodeMustGenerate) \
352-
macro(RegExpExecNonGlobalOrSticky, NodeResultJS) \
352+
macro(RegExpExecNonGlobalOrSticky, NodeResultJS | NodeMustGenerate) \
353353
macro(RegExpTest, NodeResultJS | NodeMustGenerate) \
354354
macro(RegExpTestInline, NodeResultJS | NodeMustGenerate) \
355355
macro(RegExpMatchFast, NodeResultJS | NodeMustGenerate) \

0 commit comments

Comments
 (0)