[DFG] DFG should handle String#toString
authoryusukesuzuki@slowstart.org <yusukesuzuki@slowstart.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 7 Sep 2018 19:44:48 +0000 (19:44 +0000)
committeryusukesuzuki@slowstart.org <yusukesuzuki@slowstart.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 7 Sep 2018 19:44:48 +0000 (19:44 +0000)
https://bugs.webkit.org/show_bug.cgi?id=189151

Reviewed by Saam Barati.

JSTests:

The error message in String#toString and String#valueOf is poor, which will be
handled in a separate bug[1].

[1]: https://bugs.webkit.org/show_bug.cgi?id=189357

* microbenchmarks/string-object-to-string.js: Added.
(test):
* microbenchmarks/string-object-value-of.js: Added.
(test):
* stress/string-to-string-error.js: Added.
(shouldThrow):
(test):
* stress/string-to-string.js: Added.
(shouldBe):
(test1):
(test2):
(test3):
* stress/string-value-of-error.js: Added.
(shouldThrow):
(test):
* stress/string-value-of.js: Added.
(shouldBe):
(test1):
(test2):
(test3):

Source/JavaScriptCore:

We handle String#toString and String#valueOf in DFG by introducing StringValueOf node.
In the fixup phase, we attempt to lower StringValueOf to the existing ToString or Identity
nodes. If we fail to lower it, we have StringValueOf(UntypedUse), which may raise an error
if an argument is neither String nor StringObject. The error message in String#toString and
String#valueOf is poor, which will be handled in a separate bug[1].

It improves simple microbenchmarks by 53.4 - 67.6%.

                                      baseline                  patched

    string-object-to-string       21.7308+-3.3147     ^     12.9655+-0.0527        ^ definitely 1.6760x faster
    string-object-value-of        20.1122+-0.0691     ^     13.1134+-0.2482        ^ definitely 1.5337x faster

[1]: https://bugs.webkit.org/show_bug.cgi?id=189357

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixupStringValueOf):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToToString):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructorOrStringValueOf):
(JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructor): Deleted.
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileToStringOrCallStringConstructorOrStringValueOf):
(JSC::FTL::DFG::LowerDFGToB3::compileToStringOrCallStringConstructor): Deleted.

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

27 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/string-object-to-string.js [new file with mode: 0644]
JSTests/microbenchmarks/string-object-value-of.js [new file with mode: 0644]
JSTests/stress/string-to-string-error.js [new file with mode: 0644]
JSTests/stress/string-to-string.js [new file with mode: 0644]
JSTests/stress/string-value-of-error.js [new file with mode: 0644]
JSTests/stress/string-value-of.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/StringConstructor.cpp
Source/JavaScriptCore/runtime/StringConstructor.h

index 4f4bdaa..68a3c7d 100644 (file)
@@ -1,3 +1,36 @@
+2018-09-06  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
+
+        [DFG] DFG should handle String#toString
+        https://bugs.webkit.org/show_bug.cgi?id=189151
+
+        Reviewed by Saam Barati.
+
+        The error message in String#toString and String#valueOf is poor, which will be
+        handled in a separate bug[1].
+
+        [1]: https://bugs.webkit.org/show_bug.cgi?id=189357
+
+        * microbenchmarks/string-object-to-string.js: Added.
+        (test):
+        * microbenchmarks/string-object-value-of.js: Added.
+        (test):
+        * stress/string-to-string-error.js: Added.
+        (shouldThrow):
+        (test):
+        * stress/string-to-string.js: Added.
+        (shouldBe):
+        (test1):
+        (test2):
+        (test3):
+        * stress/string-value-of-error.js: Added.
+        (shouldThrow):
+        (test):
+        * stress/string-value-of.js: Added.
+        (shouldBe):
+        (test1):
+        (test2):
+        (test3):
+
 2018-09-06  Michael Saboff  <msaboff@apple.com>
 
         Improper speculation type for Math.pow(NaN, 0) in Abstract Interpreter
diff --git a/JSTests/microbenchmarks/string-object-to-string.js b/JSTests/microbenchmarks/string-object-to-string.js
new file mode 100644 (file)
index 0000000..8d86655
--- /dev/null
@@ -0,0 +1,15 @@
+const chars = 'abcdefghijklmnopqrstuvwxyz';
+var prim = '';
+for (var i = 0; i < 32768; i++) {
+    prim += chars.charAt(~~(Math.random() * 26));
+}
+const obj = new String(prim);
+
+function test(obj)
+{
+    return obj.toString();
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+    test(obj);
diff --git a/JSTests/microbenchmarks/string-object-value-of.js b/JSTests/microbenchmarks/string-object-value-of.js
new file mode 100644 (file)
index 0000000..b71eb9c
--- /dev/null
@@ -0,0 +1,15 @@
+const chars = 'abcdefghijklmnopqrstuvwxyz';
+var prim = '';
+for (var i = 0; i < 32768; i++) {
+    prim += chars.charAt(~~(Math.random() * 26));
+}
+const obj = new String(prim);
+
+function test(obj)
+{
+    return obj.valueOf();
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+    test(obj);
diff --git a/JSTests/stress/string-to-string-error.js b/JSTests/stress/string-to-string-error.js
new file mode 100644 (file)
index 0000000..7f98113
--- /dev/null
@@ -0,0 +1,45 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+var toString = String.prototype.toString;
+function test(string)
+{
+    return toString.call(string);
+}
+noInline(test);
+
+var object = {};
+var symbol = Symbol("Cocoa");
+for (var i = 0; i < 3e3; ++i) {
+    shouldThrow(() => test(object), `TypeError: Type error`);
+    shouldThrow(() => test(false), `TypeError: Type error`);
+    shouldThrow(() => test(true), `TypeError: Type error`);
+    shouldThrow(() => test(42), `TypeError: Type error`);
+    shouldThrow(() => test(null), `TypeError: Type error`);
+    shouldThrow(() => test(undefined), `TypeError: Type error`);
+    shouldThrow(() => test(symbol), `TypeError: Type error`);
+}
+
+var string = "Hello";
+var stringObject = new String(string);
+for (var i = 0; i < 1e2; ++i) {
+    shouldBe(test(string), string);
+    shouldBe(test(stringObject), string);
+}
diff --git a/JSTests/stress/string-to-string.js b/JSTests/stress/string-to-string.js
new file mode 100644 (file)
index 0000000..ba0e7e3
--- /dev/null
@@ -0,0 +1,38 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test1(string)
+{
+    return string.toString();
+}
+noInline(test1);
+
+function test2(string)
+{
+    return string.toString();
+}
+noInline(test2);
+
+function test3(string)
+{
+    return string.toString();
+}
+noInline(test3);
+
+var string = "Hello";
+var stringObject = new String(string);
+
+for (var i = 0; i < 1e6; ++i) {
+    shouldBe(test1(string), string);
+    shouldBe(test2(stringObject), string);
+    if (i & 1)
+        shouldBe(test3(string), string);
+    else
+        shouldBe(test3(stringObject), string);
+}
+
+shouldBe(test1({}), `[object Object]`);
+shouldBe(test2({}), `[object Object]`);
+shouldBe(test3({}), `[object Object]`);
diff --git a/JSTests/stress/string-value-of-error.js b/JSTests/stress/string-value-of-error.js
new file mode 100644 (file)
index 0000000..53bca53
--- /dev/null
@@ -0,0 +1,45 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+var valueOf = String.prototype.valueOf;
+function test(string)
+{
+    return valueOf.call(string);
+}
+noInline(test);
+
+var object = {};
+var symbol = Symbol("Cocoa");
+for (var i = 0; i < 3e3; ++i) {
+    shouldThrow(() => test(object), `TypeError: Type error`);
+    shouldThrow(() => test(false), `TypeError: Type error`);
+    shouldThrow(() => test(true), `TypeError: Type error`);
+    shouldThrow(() => test(42), `TypeError: Type error`);
+    shouldThrow(() => test(null), `TypeError: Type error`);
+    shouldThrow(() => test(undefined), `TypeError: Type error`);
+    shouldThrow(() => test(symbol), `TypeError: Type error`);
+}
+
+var string = "Hello";
+var stringObject = new String(string);
+for (var i = 0; i < 1e2; ++i) {
+    shouldBe(test(string), string);
+    shouldBe(test(stringObject), string);
+}
diff --git a/JSTests/stress/string-value-of.js b/JSTests/stress/string-value-of.js
new file mode 100644 (file)
index 0000000..4f6d2fd
--- /dev/null
@@ -0,0 +1,39 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test1(string)
+{
+    return string.valueOf();
+}
+noInline(test1);
+
+function test2(string)
+{
+    return string.valueOf();
+}
+noInline(test2);
+
+function test3(string)
+{
+    return string.valueOf();
+}
+noInline(test3);
+
+var string = "Hello";
+var stringObject = new String(string);
+
+for (var i = 0; i < 1e6; ++i) {
+    shouldBe(test1(string), string);
+    shouldBe(test2(stringObject), string);
+    if (i & 1)
+        shouldBe(test3(string), string);
+    else
+        shouldBe(test3(stringObject), string);
+}
+
+var object = {};
+shouldBe(test1(object), object);
+shouldBe(test2(object), object);
+shouldBe(test3(object), object);
index 99f72dd..831eb56 100644 (file)
@@ -1,3 +1,59 @@
+2018-09-06  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
+
+        [DFG] DFG should handle String#toString
+        https://bugs.webkit.org/show_bug.cgi?id=189151
+
+        Reviewed by Saam Barati.
+
+        We handle String#toString and String#valueOf in DFG by introducing StringValueOf node.
+        In the fixup phase, we attempt to lower StringValueOf to the existing ToString or Identity
+        nodes. If we fail to lower it, we have StringValueOf(UntypedUse), which may raise an error
+        if an argument is neither String nor StringObject. The error message in String#toString and
+        String#valueOf is poor, which will be handled in a separate bug[1].
+
+        It improves simple microbenchmarks by 53.4 - 67.6%.
+
+                                              baseline                  patched
+
+            string-object-to-string       21.7308+-3.3147     ^     12.9655+-0.0527        ^ definitely 1.6760x faster
+            string-object-value-of        20.1122+-0.0691     ^     13.1134+-0.2482        ^ definitely 1.5337x faster
+
+        [1]: https://bugs.webkit.org/show_bug.cgi?id=189357
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::fixupStringValueOf):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::convertToToString):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructorOrStringValueOf):
+        (JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructor): Deleted.
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileToStringOrCallStringConstructorOrStringValueOf):
+        (JSC::FTL::DFG::LowerDFGToB3::compileToStringOrCallStringConstructor): Deleted.
+
 2018-09-07  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
 
         [WebAssembly] Optimize JS to Wasm call by using pointer of Signature as SignatureIndex
index 497d772..e53dd96 100644 (file)
@@ -1175,6 +1175,12 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
 
+    case StringValueOf: {
+        clobberWorld();
+        setTypeForNode(node, SpecString);
+        break;
+    }
+
     case StringSlice: {
         setTypeForNode(node, SpecString);
         break;
index a8f932c..6e9b6fc 100644 (file)
@@ -2691,6 +2691,13 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         return true;
     }
 
+    case StringPrototypeValueOfIntrinsic: {
+        insertChecks();
+        Node* value = get(virtualRegisterForArgument(0, registerOffset));
+        set(VirtualRegister(resultOperand), addToGraph(StringValueOf, value));
+        return true;
+    }
+
     case StringPrototypeReplaceIntrinsic: {
         if (argumentCountIncludingThis != 3)
             return false;
index b02f2c0..4fd9db6 100644 (file)
@@ -652,6 +652,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case NumberToStringWithRadix:
     case CreateThis:
     case InstanceOf:
+    case StringValueOf:
         read(World);
         write(Heap);
         return;
index 2b0d4df..cb167f0 100644 (file)
@@ -363,6 +363,7 @@ bool doesGC(Graph& graph, Node* node)
     case StringReplace:
     case StringReplaceRegExp:
     case StringSlice:
+    case StringValueOf:
     case CreateRest:
     case ToLowerCase:
     case CallDOMGetter:
index c44db1a..49881dc 100644 (file)
@@ -1994,6 +1994,11 @@ private:
             break;
         }
 
+        case StringValueOf: {
+            fixupStringValueOf(node);
+            break;
+        }
+
         case StringSlice: {
             fixEdge<StringUse>(node->child1());
             fixEdge<Int32Use>(node->child2());
@@ -2753,6 +2758,31 @@ private:
         }
     }
 
+    void fixupStringValueOf(Node* node)
+    {
+        if (node->child1()->shouldSpeculateString()) {
+            fixEdge<StringUse>(node->child1());
+            node->convertToIdentity();
+            return;
+        }
+
+        if (node->child1()->shouldSpeculateStringObject()) {
+            fixEdge<StringObjectUse>(node->child1());
+            node->convertToToString();
+            // It does not need to look up a toString property for the StringObject case. So we can clear NodeMustGenerate.
+            node->clearFlags(NodeMustGenerate);
+            return;
+        }
+
+        if (node->child1()->shouldSpeculateStringOrStringObject()) {
+            fixEdge<StringOrStringObjectUse>(node->child1());
+            node->convertToToString();
+            // It does not need to look up a toString property for the StringObject case. So we can clear NodeMustGenerate.
+            node->clearFlags(NodeMustGenerate);
+            return;
+        }
+    }
+
     bool attemptToMakeFastStringAdd(Node* node)
     {
         bool goodToGo = true;
index f7aa9f7..7889f3f 100644 (file)
@@ -694,7 +694,7 @@ public:
     
     void convertToToString()
     {
-        ASSERT(m_op == ToPrimitive);
+        ASSERT(m_op == ToPrimitive || m_op == StringValueOf);
         m_op = ToString;
     }
 
index af3db34..d30c925 100644 (file)
@@ -465,6 +465,7 @@ namespace JSC { namespace DFG {
     macro(WeakMapSet, NodeMustGenerate | NodeHasVarArgs) \
     macro(ExtractValueFromWeakMapGet, NodeResultJS) \
     \
+    macro(StringValueOf, NodeMustGenerate | NodeResultJS) \
     macro(StringSlice, NodeResultJS) \
     macro(ToLowerCase, NodeResultJS) \
     /* Nodes for DOM JIT */\
index 6a4c0c5..2c011e8 100644 (file)
@@ -2013,6 +2013,24 @@ StringImpl* JIT_OPERATION operationResolveRope(ExecState* exec, JSString* string
     return string->value(exec).impl();
 }
 
+JSString* JIT_OPERATION operationStringValueOf(ExecState* exec, EncodedJSValue encodedArgument)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue argument = JSValue::decode(encodedArgument);
+
+    if (argument.isString())
+        return asString(argument);
+
+    if (auto* stringObject = jsDynamicCast<StringObject*>(vm, argument))
+        return stringObject->internalValue();
+
+    throwVMTypeError(exec, scope);
+    return nullptr;
+}
+
 JSCell* JIT_OPERATION operationStringSubstr(ExecState* exec, JSCell* cell, int32_t from, int32_t span)
 {
     VM& vm = exec->vm();
@@ -2128,7 +2146,7 @@ JSCell* JIT_OPERATION operationNewStringObject(ExecState* exec, JSString* string
     return StringObject::create(vm, structure, string);
 }
 
-JSCell* JIT_OPERATION operationToStringOnCell(ExecState* exec, JSCell* cell)
+JSString* JIT_OPERATION operationToStringOnCell(ExecState* exec, JSCell* cell)
 {
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
@@ -2136,7 +2154,7 @@ JSCell* JIT_OPERATION operationToStringOnCell(ExecState* exec, JSCell* cell)
     return JSValue(cell).toString(exec);
 }
 
-JSCell* JIT_OPERATION operationToString(ExecState* exec, EncodedJSValue value)
+JSString* JIT_OPERATION operationToString(ExecState* exec, EncodedJSValue value)
 {
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
@@ -2144,7 +2162,7 @@ JSCell* JIT_OPERATION operationToString(ExecState* exec, EncodedJSValue value)
     return JSValue::decode(value).toString(exec);
 }
 
-JSCell* JIT_OPERATION operationCallStringConstructorOnCell(ExecState* exec, JSCell* cell)
+JSString* JIT_OPERATION operationCallStringConstructorOnCell(ExecState* exec, JSCell* cell)
 {
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
@@ -2152,7 +2170,7 @@ JSCell* JIT_OPERATION operationCallStringConstructorOnCell(ExecState* exec, JSCe
     return stringConstructor(exec, cell);
 }
 
-JSCell* JIT_OPERATION operationCallStringConstructor(ExecState* exec, EncodedJSValue value)
+JSString* JIT_OPERATION operationCallStringConstructor(ExecState* exec, EncodedJSValue value)
 {
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
@@ -2160,7 +2178,7 @@ JSCell* JIT_OPERATION operationCallStringConstructor(ExecState* exec, EncodedJSV
     return stringConstructor(exec, JSValue::decode(value));
 }
 
-JSCell* JIT_OPERATION operationMakeRope2(ExecState* exec, JSString* left, JSString* right)
+JSString* JIT_OPERATION operationMakeRope2(ExecState* exec, JSString* left, JSString* right)
 {
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
@@ -2168,7 +2186,7 @@ JSCell* JIT_OPERATION operationMakeRope2(ExecState* exec, JSString* left, JSStri
     return jsString(exec, left, right);
 }
 
-JSCell* JIT_OPERATION operationMakeRope3(ExecState* exec, JSString* a, JSString* b, JSString* c)
+JSString* JIT_OPERATION operationMakeRope3(ExecState* exec, JSString* a, JSString* b, JSString* c)
 {
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
@@ -2176,7 +2194,7 @@ JSCell* JIT_OPERATION operationMakeRope3(ExecState* exec, JSString* a, JSString*
     return jsString(exec, a, b, c);
 }
 
-JSCell* JIT_OPERATION operationStrCat2(ExecState* exec, EncodedJSValue a, EncodedJSValue b)
+JSString* JIT_OPERATION operationStrCat2(ExecState* exec, EncodedJSValue a, EncodedJSValue b)
 {
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
@@ -2193,7 +2211,7 @@ JSCell* JIT_OPERATION operationStrCat2(ExecState* exec, EncodedJSValue a, Encode
     return jsString(exec, str1, str2);
 }
     
-JSCell* JIT_OPERATION operationStrCat3(ExecState* exec, EncodedJSValue a, EncodedJSValue b, EncodedJSValue c)
+JSString* JIT_OPERATION operationStrCat3(ExecState* exec, EncodedJSValue a, EncodedJSValue b, EncodedJSValue c)
 {
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
index 35bf6bc..6cb515c 100644 (file)
@@ -193,6 +193,7 @@ StringImpl* JIT_OPERATION operationResolveRope(ExecState*, JSString*);
 JSString* JIT_OPERATION operationSingleCharacterString(ExecState*, int32_t);
 
 JSCell* JIT_OPERATION operationStringSubstr(ExecState*, JSCell*, int32_t, int32_t);
+JSString* JIT_OPERATION operationStringValueOf(ExecState*, EncodedJSValue);
 JSString* JIT_OPERATION operationToLowerCase(ExecState*, JSString*, uint32_t);
 
 char* JIT_OPERATION operationInt32ToString(ExecState*, int32_t, int32_t);
@@ -212,14 +213,14 @@ EncodedJSValue JIT_OPERATION operationParseIntString(ExecState*, JSString*, int3
 EncodedJSValue JIT_OPERATION operationParseIntGeneric(ExecState*, EncodedJSValue, int32_t);
 
 JSCell* JIT_OPERATION operationNewStringObject(ExecState*, JSString*, Structure*);
-JSCell* JIT_OPERATION operationToStringOnCell(ExecState*, JSCell*);
-JSCell* JIT_OPERATION operationToString(ExecState*, EncodedJSValue);
-JSCell* JIT_OPERATION operationCallStringConstructorOnCell(ExecState*, JSCell*);
-JSCell* JIT_OPERATION operationCallStringConstructor(ExecState*, EncodedJSValue);
-JSCell* JIT_OPERATION operationMakeRope2(ExecState*, JSString*, JSString*);
-JSCell* JIT_OPERATION operationMakeRope3(ExecState*, JSString*, JSString*, JSString*);
-JSCell* JIT_OPERATION operationStrCat2(ExecState*, EncodedJSValue, EncodedJSValue);
-JSCell* JIT_OPERATION operationStrCat3(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
+JSString* JIT_OPERATION operationToStringOnCell(ExecState*, JSCell*);
+JSString* JIT_OPERATION operationToString(ExecState*, EncodedJSValue);
+JSString* JIT_OPERATION operationCallStringConstructorOnCell(ExecState*, JSCell*);
+JSString* JIT_OPERATION operationCallStringConstructor(ExecState*, EncodedJSValue);
+JSString* JIT_OPERATION operationMakeRope2(ExecState*, JSString*, JSString*);
+JSString* JIT_OPERATION operationMakeRope3(ExecState*, JSString*, JSString*, JSString*);
+JSString* JIT_OPERATION operationStrCat2(ExecState*, EncodedJSValue, EncodedJSValue);
+JSString* JIT_OPERATION operationStrCat3(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
 char* JIT_OPERATION operationFindSwitchImmTargetForDouble(ExecState*, EncodedJSValue, size_t tableIndex);
 char* JIT_OPERATION operationSwitchString(ExecState*, size_t tableIndex, JSString*);
 int32_t JIT_OPERATION operationSwitchStringAndGetBranchOffset(ExecState*, size_t tableIndex, JSString*);
index a8b92cb..f37fd06 100644 (file)
@@ -826,6 +826,7 @@ private:
             break;
         }
 
+        case StringValueOf:
         case StringSlice:
         case ToLowerCase:
             setPrediction(SpecString);
index 4bfb615..60801d0 100644 (file)
@@ -440,6 +440,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case ResolveScope:
     case MapHash:
     case NormalizeMapKey:
+    case StringValueOf:
     case StringSlice:
     case ToLowerCase:
     case GetMapBucket:
index 15c9291..bdb4db7 100644 (file)
@@ -9233,8 +9233,9 @@ GPRReg SpeculativeJIT::temporaryRegisterForPutByVal(GPRTemporary& temporary, Arr
     return temporary.gpr();
 }
 
-void SpeculativeJIT::compileToStringOrCallStringConstructor(Node* node)
+void SpeculativeJIT::compileToStringOrCallStringConstructorOrStringValueOf(Node* node)
 {
+    ASSERT(node->op() != StringValueOf || node->child1().useKind() == UntypedUse);
     switch (node->child1().useKind()) {
     case NotCellUse: {
         JSValueOperand op1(this, node->child1(), ManualOperandSpeculation);
@@ -9279,6 +9280,8 @@ void SpeculativeJIT::compileToStringOrCallStringConstructor(Node* node)
         }
         if (node->op() == ToString)
             callOperation(operationToString, resultGPR, op1Regs);
+        else if (node->op() == StringValueOf)
+            callOperation(operationStringValueOf, resultGPR, op1Regs);
         else {
             ASSERT(node->op() == CallStringConstructor);
             callOperation(operationCallStringConstructor, resultGPR, op1Regs);
index 7fefd12..e5c30af 100644 (file)
@@ -1248,7 +1248,7 @@ public:
     void emitSwitchString(Node*, SwitchData*);
     void emitSwitch(Node*);
     
-    void compileToStringOrCallStringConstructor(Node*);
+    void compileToStringOrCallStringConstructorOrStringValueOf(Node*);
     void compileNumberToStringWithRadix(Node*);
     void compileNumberToStringWithValidRadixConstant(Node*);
     void compileNumberToStringWithValidRadixConstant(Node*, int32_t radix);
index 59ce3a1..166e4a4 100644 (file)
@@ -3077,8 +3077,9 @@ void SpeculativeJIT::compile(Node* node)
     }
         
     case ToString:
-    case CallStringConstructor: {
-        compileToStringOrCallStringConstructor(node);
+    case CallStringConstructor:
+    case StringValueOf: {
+        compileToStringOrCallStringConstructorOrStringValueOf(node);
         break;
     }
         
index 025b41b..121fd63 100644 (file)
@@ -3326,8 +3326,9 @@ void SpeculativeJIT::compile(Node* node)
     }
         
     case ToString:
-    case CallStringConstructor: {
-        compileToStringOrCallStringConstructor(node);
+    case CallStringConstructor:
+    case StringValueOf: {
+        compileToStringOrCallStringConstructorOrStringValueOf(node);
         break;
     }
         
index f1822a4..3e4afad 100644 (file)
@@ -321,6 +321,7 @@ inline CapabilityLevel canCompile(Node* node)
     case SameValue:
     case DefineDataProperty:
     case DefineAccessorProperty:
+    case StringValueOf:
     case StringSlice:
     case ToLowerCase:
     case NumberToStringWithRadix:
index 6516636..707bd77 100644 (file)
@@ -891,7 +891,8 @@ private:
             break;
         case ToString:
         case CallStringConstructor:
-            compileToStringOrCallStringConstructor();
+        case StringValueOf:
+            compileToStringOrCallStringConstructorOrStringValueOf();
             break;
         case ToPrimitive:
             compileToPrimitive();
@@ -6119,8 +6120,9 @@ private:
         }
     }
     
-    void compileToStringOrCallStringConstructor()
+    void compileToStringOrCallStringConstructorOrStringValueOf()
     {
+        ASSERT(m_node->op() != StringValueOf || m_node->child1().useKind() == UntypedUse);
         switch (m_node->child1().useKind()) {
         case StringObjectUse: {
             LValue cell = lowCell(m_node->child1());
@@ -6191,10 +6193,14 @@ private:
             
             m_out.appendTo(notString, continuation);
             LValue operation;
-            if (m_node->child1().useKind() == CellUse)
+            if (m_node->child1().useKind() == CellUse) {
+                ASSERT(m_node->op() != StringValueOf);
                 operation = m_out.operation(m_node->op() == ToString ? operationToStringOnCell : operationCallStringConstructorOnCell);
-            else
-                operation = m_out.operation(m_node->op() == ToString ? operationToString : operationCallStringConstructor);
+            } else {
+                operation = m_out.operation(m_node->op() == ToString
+                    ? operationToString : m_node->op() == StringValueOf
+                    ? operationStringValueOf : operationCallStringConstructor);
+            }
             ValueFromBlock convertedResult = m_out.anchor(vmCall(Int64, operation, m_callFrame, value));
             m_out.jump(continuation);
             
index adf4fe2..dc284e1 100644 (file)
@@ -93,7 +93,7 @@ static EncodedJSValue JSC_HOST_CALL stringFromCharCode(ExecState* exec)
     return JSValue::encode(jsString(exec, WTFMove(impl)));
 }
 
-JSCell* JSC_HOST_CALL stringFromCharCode(ExecState* exec, int32_t arg)
+JSString* JSC_HOST_CALL stringFromCharCode(ExecState* exec, int32_t arg)
 {
     return jsSingleCharacterString(exec, arg);
 }
@@ -144,7 +144,7 @@ static EncodedJSValue JSC_HOST_CALL constructWithStringConstructor(ExecState* ex
     return JSValue::encode(StringObject::create(vm, structure, str));
 }
 
-JSCell* stringConstructor(ExecState* exec, JSValue argument)
+JSString* stringConstructor(ExecState* exec, JSValue argument)
 {
     if (argument.isSymbol())
         return jsNontrivialString(exec, asSymbol(argument)->descriptiveString());
index ffc0e80..12905fb 100644 (file)
@@ -51,7 +51,7 @@ private:
     void finishCreation(VM&, StringPrototype*);
 };
 
-JSCell* JSC_HOST_CALL stringFromCharCode(ExecState*, int32_t);
-JSCell* stringConstructor(ExecState*, JSValue);
+JSString* JSC_HOST_CALL stringFromCharCode(ExecState*, int32_t);
+JSString* stringConstructor(ExecState*, JSValue);
 
 } // namespace JSC