"" + 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/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 5d756e3..17b7da0 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,25 @@
+2013-03-20  Filip Pizlo  <fpizlo@apple.com>
+
+        "" + 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):
+
 2013-03-20  Zoltan Herczeg  <zherczeg@webkit.org>
 
         ARMv7 replaceWithJump ASSERT failure after r135330.
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)
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index c32f846..a72e7ab 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -34,6 +34,17 @@
 
 namespace JSC { namespace DFG {
 
+SpeculatedType resultOfToPrimitive(SpeculatedType type)
+{
+    if (type & SpecObject) {
+        // Objects get turned into strings. So if the input has hints of objectness,
+        // the output will have hinsts of stringiness.
+        return mergeSpeculations(type & ~SpecObject, SpecString);
+    }
+    
+    return type;
+}
+
 class PredictionPropagationPhase : public Phase {
 public:
     PredictionPropagationPhase(Graph& graph)
@@ -421,22 +432,8 @@
             
         case ToPrimitive: {
             SpeculatedType child = node->child1()->prediction();
-            if (child) {
-                if (isObjectSpeculation(child)) {
-                    // I'd love to fold this case into the case below, but I can't, because
-                    // removing SpecObject from something that only has an object
-                    // prediction and nothing else means we have an ill-formed SpeculatedType
-                    // (strong predict-none). This should be killed once we remove all traces
-                    // of static (aka weak) predictions.
-                    changed |= mergePrediction(SpecString);
-                } else if (child & SpecObject) {
-                    // Objects get turned into strings. So if the input has hints of objectness,
-                    // the output will have hinsts of stringiness.
-                    changed |= mergePrediction(
-                        mergeSpeculations(child & ~SpecObject, SpecString));
-                } else
-                    changed |= mergePrediction(child);
-            }
+            if (child)
+                changed |= mergePrediction(resultOfToPrimitive(child));
             break;
         }
             
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.h b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.h
index ae025cd..29fe845 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.h
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.h
@@ -30,6 +30,8 @@
 
 #if ENABLE(DFG_JIT)
 
+#include "SpeculatedType.h"
+
 namespace JSC { namespace DFG {
 
 class Graph;
@@ -45,6 +47,9 @@
 
 bool performPredictionPropagation(Graph&);
 
+// Helper used for FixupPhase for computing the predicted type of a ToPrimitive.
+SpeculatedType resultOfToPrimitive(SpeculatedType type);
+
 } } // namespace JSC::DFG::Phase
 
 #endif // ENABLE(DFG_JIT)