FTL should simplify StringReplace with an empty replacement string
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 1 Mar 2016 23:35:05 +0000 (23:35 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 1 Mar 2016 23:35:05 +0000 (23:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=154871

Reviewed by Michael Saboff.

This is a simple and hugely profitable change. If we do a string.replace(/things/, ""), then
this calls directly into StringPrototype's replace-with-empty-string logic instead of going
through stuff that does checks before reaching that same conclusion.

This speeds up Octane/regexp by about 6-10%. It also speeds up the attached microbenchmark by
about 7%.

* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileStringReplace):
* runtime/StringPrototype.cpp:
(JSC::jsSpliceSubstringsWithSeparators):
(JSC::removeUsingRegExpSearch):
(JSC::replaceUsingRegExpSearch):
(JSC::operationStringProtoFuncReplaceRegExpEmptyStr):
(JSC::operationStringProtoFuncReplaceRegExpString):
* runtime/StringPrototype.h:

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

LayoutTests/js/regress/script-tests/string-replace.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/StringPrototype.cpp
Source/JavaScriptCore/runtime/StringPrototype.h

index 2a31929..56e152c 100644 (file)
@@ -1,7 +1,7 @@
 (function() {
     var result;
     for (var i = 0; i < 400000; ++i) {
 (function() {
     var result;
     for (var i = 0; i < 400000; ++i) {
-        result = "foo".replace(/f/, "b");
+        result = "foo".replace(/f/g, "b");
     }
     if (result != "boo")
         throw "Error: bad result: "+ result;
     }
     if (result != "boo")
         throw "Error: bad result: "+ result;
index 906da00..911b1f5 100644 (file)
@@ -1,3 +1,27 @@
+2016-03-01  Filip Pizlo  <fpizlo@apple.com>
+
+        FTL should simplify StringReplace with an empty replacement string
+        https://bugs.webkit.org/show_bug.cgi?id=154871
+
+        Reviewed by Michael Saboff.
+
+        This is a simple and hugely profitable change. If we do a string.replace(/things/, ""), then
+        this calls directly into StringPrototype's replace-with-empty-string logic instead of going
+        through stuff that does checks before reaching that same conclusion.
+
+        This speeds up Octane/regexp by about 6-10%. It also speeds up the attached microbenchmark by
+        about 7%.
+
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileStringReplace):
+        * runtime/StringPrototype.cpp:
+        (JSC::jsSpliceSubstringsWithSeparators):
+        (JSC::removeUsingRegExpSearch):
+        (JSC::replaceUsingRegExpSearch):
+        (JSC::operationStringProtoFuncReplaceRegExpEmptyStr):
+        (JSC::operationStringProtoFuncReplaceRegExpString):
+        * runtime/StringPrototype.h:
+
 2016-03-01  Alex Christensen  <achristensen@webkit.org>
 
         Reduce size of internal windows build output
 2016-03-01  Alex Christensen  <achristensen@webkit.org>
 
         Reduce size of internal windows build output
index 5f89d2c..a25f6a1 100644 (file)
@@ -6467,6 +6467,21 @@ private:
         if (m_node->child1().useKind() == StringUse
             && m_node->child2().useKind() == RegExpObjectUse
             && m_node->child3().useKind() == StringUse) {
         if (m_node->child1().useKind() == StringUse
             && m_node->child2().useKind() == RegExpObjectUse
             && m_node->child3().useKind() == StringUse) {
+
+            if (JSString* replace = m_node->child3()->dynamicCastConstant<JSString*>()) {
+                if (!replace->length()) {
+                    LValue string = lowString(m_node->child1());
+                    LValue regExp = lowCell(m_node->child2());
+                    speculateRegExpObject(m_node->child2(), regExp);
+
+                    LValue result = vmCall(
+                        Int64, m_out.operation(operationStringProtoFuncReplaceRegExpEmptyStr),
+                        m_callFrame, string, regExp);
+
+                    setJSValue(result);
+                    return;
+                }
+            }
             
             LValue string = lowString(m_node->child1());
             LValue regExp = lowCell(m_node->child2());
             
             LValue string = lowString(m_node->child1());
             LValue regExp = lowCell(m_node->child2());
index 22d2682..3ba6cff 100644 (file)
@@ -446,7 +446,7 @@ static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, J
     return jsString(exec, impl.release());
 }
 
     return jsString(exec, impl.release());
 }
 
-static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const String& source, RegExp* regExp)
+static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const String& source, RegExp* regExp)
 {
     size_t lastIndex = 0;
     unsigned startPosition = 0;
 {
     size_t lastIndex = 0;
     unsigned startPosition = 0;
@@ -506,6 +506,8 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
             return removeUsingRegExpSearch(exec, string, source, regExp);
     }
 
             return removeUsingRegExpSearch(exec, string, source, regExp);
     }
 
+    // FIXME: This is wrong because we may be called directly from the FTL.
+    // https://bugs.webkit.org/show_bug.cgi?id=154874
     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
 
     size_t lastIndex = 0;
     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
 
     size_t lastIndex = 0;
@@ -664,6 +666,24 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
     return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
 }
 
     return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
 }
 
+EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpEmptyStr(
+    ExecState* exec, JSString* thisValue, RegExpObject* searchValue)
+{
+    RegExp* regExp = searchValue->regExp();
+    if (regExp->global()) {
+        // ES5.1 15.5.4.10 step 8.a.
+        searchValue->setLastIndex(exec, 0);
+        if (exec->hadException())
+            return JSValue::encode(jsUndefined());
+        return removeUsingRegExpSearch(exec, thisValue, thisValue->value(exec), regExp);
+    }
+
+    CallData callData;
+    String replacementString = emptyString();
+    return replaceUsingRegExpSearch(
+        exec, thisValue, searchValue, callData, CallTypeNone, replacementString, JSValue());
+}
+
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
     ExecState* exec, JSString* thisValue, RegExpObject* searchValue, JSString* replaceString)
 {
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
     ExecState* exec, JSString* thisValue, RegExpObject* searchValue, JSString* replaceString)
 {
index b94da81..1855cd9 100644 (file)
@@ -54,11 +54,13 @@ private:
 };
 
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
 };
 
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
-    ExecState* exec, EncodedJSValue thisValue, EncodedJSValue searchValue,
-    EncodedJSValue replaceValue);
+    ExecState*, EncodedJSValue thisValue, EncodedJSValue searchValue, EncodedJSValue replaceValue);
+
+EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpEmptyStr(
+    ExecState*, JSString* thisValue, RegExpObject* searchValue);
 
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
 
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
-    ExecState* exec, JSString* thisValue, RegExpObject* searchValue, JSString* replaceValue);
+    ExecState*, JSString* thisValue, RegExpObject* searchValue, JSString* replaceValue);
 
 } // namespace JSC
 
 
 } // namespace JSC