[JSC] Implement String.prototype.concat in JS builtins
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 1 Jun 2017 05:18:25 +0000 (05:18 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 1 Jun 2017 05:18:25 +0000 (05:18 +0000)
https://bugs.webkit.org/show_bug.cgi?id=172798

Reviewed by Sam Weinig.

JSTests:

* microbenchmarks/string-concat-convert.js: Added.
(test):
* microbenchmarks/string-concat-long-convert.js: Added.
(test):
* microbenchmarks/string-concat-long.js: Added.
(test):
* microbenchmarks/string-concat.js: Added.
(test):

Source/JavaScriptCore:

Since we have highly effective + operation for strings,
implementing String.prototype.concat in JS simplifies the
implementation and improves performance by using speculated
types.

Added microbenchmarks show performance improvement.

string-concat-long-convert     1063.2787+-12.9101    ^    109.0855+-2.8083        ^ definitely 9.7472x faster
string-concat-convert          1111.1366+-12.2363    ^     99.3402+-1.9874        ^ definitely 11.1852x faster
string-concat                   131.7377+-3.8359     ^     54.3949+-0.9580        ^ definitely 2.4219x faster
string-concat-long               79.4726+-1.9644     ^     64.6301+-1.4941        ^ definitely 1.2297x faster

* builtins/StringPrototype.js:
(globalPrivate.stringConcatSlowPath):
(concat):
* runtime/StringPrototype.cpp:
(JSC::StringPrototype::finishCreation):
(JSC::stringProtoFuncConcat): Deleted.

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

JSTests/ChangeLog
JSTests/microbenchmarks/string-concat-convert.js [new file with mode: 0644]
JSTests/microbenchmarks/string-concat-long-convert.js [new file with mode: 0644]
JSTests/microbenchmarks/string-concat-long.js [new file with mode: 0644]
JSTests/microbenchmarks/string-concat.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/StringPrototype.js
Source/JavaScriptCore/runtime/StringPrototype.cpp

index 90fc782..f03c936 100644 (file)
@@ -1,3 +1,19 @@
+2017-05-31  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Implement String.prototype.concat in JS builtins
+        https://bugs.webkit.org/show_bug.cgi?id=172798
+
+        Reviewed by Sam Weinig.
+
+        * microbenchmarks/string-concat-convert.js: Added.
+        (test):
+        * microbenchmarks/string-concat-long-convert.js: Added.
+        (test):
+        * microbenchmarks/string-concat-long.js: Added.
+        (test):
+        * microbenchmarks/string-concat.js: Added.
+        (test):
+
 2017-05-31  Oleksandr Skachkov  <gskachkov@gmail.com>
 
         Rolling out: Prevent async methods named 'function'
diff --git a/JSTests/microbenchmarks/string-concat-convert.js b/JSTests/microbenchmarks/string-concat-convert.js
new file mode 100644 (file)
index 0000000..02d690b
--- /dev/null
@@ -0,0 +1,8 @@
+function test(a, b, c, d, e)
+{
+    return a.concat(b).concat(c).concat(d).concat(e);
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+    test(new String("Cocoa"), new String("Cappuccino"), new String("Matcha"), new String("Rize"), new String("Kilimanjaro"));
diff --git a/JSTests/microbenchmarks/string-concat-long-convert.js b/JSTests/microbenchmarks/string-concat-long-convert.js
new file mode 100644 (file)
index 0000000..44a9cf8
--- /dev/null
@@ -0,0 +1,8 @@
+function test(a, b, c, d, e)
+{
+    return a.concat(b, c, d, e);
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+    test(new String("Cocoa"), new String("Cappuccino"), new String("Matcha"), new String("Rize"), new String("Kilimanjaro"));
diff --git a/JSTests/microbenchmarks/string-concat-long.js b/JSTests/microbenchmarks/string-concat-long.js
new file mode 100644 (file)
index 0000000..d805e9b
--- /dev/null
@@ -0,0 +1,8 @@
+function test(a, b, c, d, e)
+{
+    return a.concat(b, c, d, e);
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+    test("Cocoa", "Cappuccino", "Matcha", "Rize", "Kilimanjaro");
diff --git a/JSTests/microbenchmarks/string-concat.js b/JSTests/microbenchmarks/string-concat.js
new file mode 100644 (file)
index 0000000..920abdb
--- /dev/null
@@ -0,0 +1,8 @@
+function test(a, b, c, d, e)
+{
+    return a.concat(b).concat(c).concat(d).concat(e);
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+    test("Cocoa", "Cappuccino", "Matcha", "Rize", "Kilimanjaro");
index 3bcc7ec..604d5da 100644 (file)
@@ -1,3 +1,29 @@
+2017-05-31  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Implement String.prototype.concat in JS builtins
+        https://bugs.webkit.org/show_bug.cgi?id=172798
+
+        Reviewed by Sam Weinig.
+
+        Since we have highly effective + operation for strings,
+        implementing String.prototype.concat in JS simplifies the
+        implementation and improves performance by using speculated
+        types.
+
+        Added microbenchmarks show performance improvement.
+
+        string-concat-long-convert     1063.2787+-12.9101    ^    109.0855+-2.8083        ^ definitely 9.7472x faster
+        string-concat-convert          1111.1366+-12.2363    ^     99.3402+-1.9874        ^ definitely 11.1852x faster
+        string-concat                   131.7377+-3.8359     ^     54.3949+-0.9580        ^ definitely 2.4219x faster
+        string-concat-long               79.4726+-1.9644     ^     64.6301+-1.4941        ^ definitely 1.2297x faster
+
+        * builtins/StringPrototype.js:
+        (globalPrivate.stringConcatSlowPath):
+        (concat):
+        * runtime/StringPrototype.cpp:
+        (JSC::StringPrototype::finishCreation):
+        (JSC::stringProtoFuncConcat): Deleted.
+
 2017-05-31  Mark Lam  <mark.lam@apple.com>
 
         Remove overrides of visitChildren() that do not add any functionality.
index 144ed1a..68d6376 100644 (file)
@@ -306,6 +306,29 @@ function split(separator, limit)
 }
 
 @globalPrivate
+function stringConcatSlowPath()
+{
+    "use strict";
+
+    var result = @toString(this);
+    for (var i = 0, length = arguments.length; i < length; ++i)
+        result += @toString(arguments[i]);
+    return result;
+}
+
+function concat(arg /* ... */)
+{
+    "use strict";
+
+    if (this == null)
+        @throwTypeError("String.prototype.concat requires that |this| not be null or undefined");
+
+    if (@argumentCount() === 1)
+        return @toString(this) + @toString(arg);
+    return @tailCallForwardArguments(@stringConcatSlowPath, this);
+}
+
+@globalPrivate
 function createHTML(func, string, tag, attribute, value)
 {
     "use strict";
index 46b56a2..5ab4aa7 100644 (file)
@@ -63,7 +63,6 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(ExecState*);
@@ -95,6 +94,7 @@ const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, &st
 
 /* Source for StringConstructor.lut.h
 @begin stringPrototypeTable
+    concat    JSBuiltin    DontEnum|Function 1
     match     JSBuiltin    DontEnum|Function 1
     padStart  JSBuiltin    DontEnum|Function 1
     padEnd    JSBuiltin    DontEnum|Function 1
@@ -134,7 +134,6 @@ void StringPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, JSStr
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charAt", stringProtoFuncCharAt, DontEnum, 1, CharAtIntrinsic);
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charCodeAt", stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("codePointAt", stringProtoFuncCodePointAt, DontEnum, 1);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("concat", stringProtoFuncConcat, DontEnum, 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("indexOf", stringProtoFuncIndexOf, DontEnum, 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", stringProtoFuncLastIndexOf, DontEnum, 1);
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceUsingRegExpPrivateName(), stringProtoFuncReplaceUsingRegExp, DontEnum, 2, StringPrototypeReplaceRegExpIntrinsic);
@@ -994,25 +993,6 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState* exec)
     return JSValue::encode(jsUndefined());
 }
 
-EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (thisValue.isString() && exec->argumentCount() == 1) {
-        JSString* str = exec->uncheckedArgument(0).toString(exec);
-        RETURN_IF_EXCEPTION(scope, encodedJSValue());
-        scope.release();
-        return JSValue::encode(jsString(exec, asString(thisValue), str));
-    }
-
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    scope.release();
-    return JSValue::encode(jsStringFromArguments(exec, thisValue));
-}
-
 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
 {
     VM& vm = exec->vm();