"" + x where x is not a string should be optimized by the DFG to some manner of ToString conversion
https://bugs.webkit.org/show_bug.cgi?id=112845

Reviewed by Mark Hahnenberg.
        
I like to do "" + x. So I decided to make DFG recognize it, and related idioms.

* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixupToPrimitive):
(FixupPhase):
(JSC::DFG::FixupPhase::fixupToString):
(JSC::DFG::FixupPhase::attemptToMakeFastStringAdd):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::resultOfToPrimitive):
(DFG):
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGPredictionPropagationPhase.h:
(DFG):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@146400 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 30318f3..4debbac 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -31,6 +31,7 @@
 #include "DFGGraph.h"
 #include "DFGInsertionSet.h"
 #include "DFGPhase.h"
+#include "DFGPredictionPropagationPhase.h"
 #include "DFGVariableAccessDataDump.h"
 #include "Operations.h"
 
@@ -555,61 +556,12 @@
         }
             
         case ToPrimitive: {
-            if (node->child1()->shouldSpeculateInteger()) {
-                setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
-                node->convertToIdentity();
-                break;
-            }
-            
-            if (node->child1()->shouldSpeculateString()) {
-                setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
-                node->convertToIdentity();
-                break;
-            }
-            
-            if (node->child1()->shouldSpeculateStringObject()
-                && canOptimizeStringObjectAccess(node->codeOrigin)) {
-                setUseKindAndUnboxIfProfitable<StringObjectUse>(node->child1());
-                node->convertToToString();
-                break;
-            }
-            
-            if (node->child1()->shouldSpeculateStringOrStringObject()
-                && canOptimizeStringObjectAccess(node->codeOrigin)) {
-                setUseKindAndUnboxIfProfitable<StringOrStringObjectUse>(node->child1());
-                node->convertToToString();
-                break;
-            }
-            
-            // FIXME: Add string speculation here.
-            // https://bugs.webkit.org/show_bug.cgi?id=110175
+            fixupToPrimitive(node);
             break;
         }
             
         case ToString: {
-            if (node->child1()->shouldSpeculateString()) {
-                setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
-                node->convertToIdentity();
-                break;
-            }
-            
-            if (node->child1()->shouldSpeculateStringObject()
-                && canOptimizeStringObjectAccess(node->codeOrigin)) {
-                setUseKindAndUnboxIfProfitable<StringObjectUse>(node->child1());
-                break;
-            }
-            
-            if (node->child1()->shouldSpeculateStringOrStringObject()
-                && canOptimizeStringObjectAccess(node->codeOrigin)) {
-                setUseKindAndUnboxIfProfitable<StringOrStringObjectUse>(node->child1());
-                break;
-            }
-            
-            if (node->child1()->shouldSpeculateCell()) {
-                setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
-                break;
-            }
-            
+            fixupToString(node);
             break;
         }
             
@@ -981,6 +933,61 @@
         }
     }
     
+    void fixupToPrimitive(Node* node)
+    {
+        if (node->child1()->shouldSpeculateInteger()) {
+            setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+            node->convertToIdentity();
+            return;
+        }
+        
+        if (node->child1()->shouldSpeculateString()) {
+            setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
+            node->convertToIdentity();
+            return;
+        }
+        
+        if (node->child1()->shouldSpeculateStringObject()
+            && canOptimizeStringObjectAccess(node->codeOrigin)) {
+            setUseKindAndUnboxIfProfitable<StringObjectUse>(node->child1());
+            node->convertToToString();
+            return;
+        }
+        
+        if (node->child1()->shouldSpeculateStringOrStringObject()
+            && canOptimizeStringObjectAccess(node->codeOrigin)) {
+            setUseKindAndUnboxIfProfitable<StringOrStringObjectUse>(node->child1());
+            node->convertToToString();
+            return;
+        }
+    }
+    
+    void fixupToString(Node* node)
+    {
+        if (node->child1()->shouldSpeculateString()) {
+            setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
+            node->convertToIdentity();
+            return;
+        }
+        
+        if (node->child1()->shouldSpeculateStringObject()
+            && canOptimizeStringObjectAccess(node->codeOrigin)) {
+            setUseKindAndUnboxIfProfitable<StringObjectUse>(node->child1());
+            return;
+        }
+        
+        if (node->child1()->shouldSpeculateStringOrStringObject()
+            && canOptimizeStringObjectAccess(node->codeOrigin)) {
+            setUseKindAndUnboxIfProfitable<StringOrStringObjectUse>(node->child1());
+            return;
+        }
+        
+        if (node->child1()->shouldSpeculateCell()) {
+            setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
+            return;
+        }
+    }
+    
     template<UseKind leftUseKind>
     bool attemptToMakeFastStringAdd(Node* node, Edge& left, Edge& right)
     {
@@ -989,32 +996,37 @@
         if (isStringObjectUse<leftUseKind>() && !canOptimizeStringObjectAccess(node->codeOrigin))
             return false;
         
-        if (right->shouldSpeculateString()) {
-            convertStringAddUse<leftUseKind>(node, left);
+        convertStringAddUse<leftUseKind>(node, left);
+        
+        if (right->shouldSpeculateString())
             convertStringAddUse<StringUse>(node, right);
-            convertToMakeRope(node);
-            return true;
-        }
-        
-        if (right->shouldSpeculateStringObject()
-            && canOptimizeStringObjectAccess(node->codeOrigin)) {
-            convertStringAddUse<leftUseKind>(node, left);
+        else if (right->shouldSpeculateStringObject() && canOptimizeStringObjectAccess(node->codeOrigin))
             convertStringAddUse<StringObjectUse>(node, right);
-            convertToMakeRope(node);
-            return true;
-        }
-        
-        if (right->shouldSpeculateStringOrStringObject()
-            && canOptimizeStringObjectAccess(node->codeOrigin)) {
-            convertStringAddUse<leftUseKind>(node, left);
+        else if (right->shouldSpeculateStringOrStringObject() && canOptimizeStringObjectAccess(node->codeOrigin))
             convertStringAddUse<StringOrStringObjectUse>(node, right);
-            convertToMakeRope(node);
-            return true;
+        else {
+            // At this point we know that the other operand is something weird. The semantically correct
+            // way of dealing with this is:
+            //
+            // MakeRope(@left, ToString(ToPrimitive(@right)))
+            //
+            // So that's what we emit. NB, we need to do all relevant type checks on @left before we do
+            // anything to @right, since ToPrimitive may be effectful.
+            
+            Node* toPrimitive = m_insertionSet.insertNode(
+                m_indexInBlock, resultOfToPrimitive(right->prediction()), ToPrimitive, node->codeOrigin,
+                Edge(right.node()));
+            Node* toString = m_insertionSet.insertNode(
+                m_indexInBlock, SpecString, ToString, node->codeOrigin, Edge(toPrimitive));
+            
+            fixupToPrimitive(toPrimitive);
+            fixupToString(toString);
+            
+            right.setNode(toString);
         }
         
-        // FIXME: We ought to be able to convert the right case to do
-        // ToPrimitiveToString.
-        return false; // Let the slow path worry about it.
+        convertToMakeRope(node);
+        return true;
     }
     
     bool isStringPrototypeMethodSane(Structure* stringPrototypeStructure, const Identifier& ident)