DFG should inline binary string concatenations (i.e. ValueAdd with string children)
https://bugs.webkit.org/show_bug.cgi?id=112599

Reviewed by Oliver Hunt.
        
This does as advertised: if you do x + y where x and y are strings, you'll get
a fast inlined JSRopeString allocation (along with whatever checks are necessary).
It also does good things if either x or y (or both) are StringObjects, or some
other thing like StringOrStringObject. It also lays the groundwork for making this
fast if either x or y are numbers, or some other reasonably-cheap-to-convert
value.

* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::executeEffects):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(FixupPhase):
(JSC::DFG::FixupPhase::isStringObjectUse):
(JSC::DFG::FixupPhase::convertStringAddUse):
(JSC::DFG::FixupPhase::attemptToMakeFastStringAdd):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileAdd):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
(SpeculativeJIT):
(JSC::DFG::SpeculativeJIT::emitAllocateJSCell):
(JSC::DFG::SpeculativeJIT::emitAllocateJSObject):
* runtime/JSString.h:
(JSC::JSString::offsetOfFlags):
(JSString):
(JSRopeString):
(JSC::JSRopeString::offsetOfFibers):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@146164 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 6e8d922..29472f0 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -148,6 +148,30 @@
                 fixDoubleEdge<NumberUse>(node->child2());
                 break;
             }
+            
+            // FIXME: Optimize for the case where one of the operands is the
+            // empty string. Also consider optimizing for the case where we don't
+            // believe either side is the emtpy string. Both of these things should
+            // be easy.
+            
+            if (node->child1()->shouldSpeculateString()
+                && attemptToMakeFastStringAdd<StringUse>(node, node->child1(), node->child2()))
+                break;
+            if (node->child2()->shouldSpeculateString()
+                && attemptToMakeFastStringAdd<StringUse>(node, node->child2(), node->child1()))
+                break;
+            if (node->child1()->shouldSpeculateStringObject()
+                && attemptToMakeFastStringAdd<StringObjectUse>(node, node->child1(), node->child2()))
+                break;
+            if (node->child2()->shouldSpeculateStringObject()
+                && attemptToMakeFastStringAdd<StringObjectUse>(node, node->child2(), node->child1()))
+                break;
+            if (node->child1()->shouldSpeculateStringOrStringObject()
+                && attemptToMakeFastStringAdd<StringOrStringObjectUse>(node, node->child1(), node->child2()))
+                break;
+            if (node->child2()->shouldSpeculateStringOrStringObject()
+                && attemptToMakeFastStringAdd<StringOrStringObjectUse>(node, node->child2(), node->child1()))
+                break;
             break;
         }
             
@@ -862,6 +886,74 @@
 #endif
     }
     
+    template<UseKind useKind>
+    bool isStringObjectUse()
+    {
+        switch (useKind) {
+        case StringObjectUse:
+        case StringOrStringObjectUse:
+            return true;
+        default:
+            return false;
+        }
+    }
+    
+    template<UseKind useKind>
+    void convertStringAddUse(Node* node, Edge& edge)
+    {
+        if (useKind == StringUse) {
+            // This preserves the binaryUseKind() invariant ot ValueAdd: ValueAdd's
+            // two edges will always have identical use kinds, which makes the
+            // decision process much easier.
+            observeUseKindOnNode<StringUse>(edge.node());
+            m_insertionSet.insertNode(
+                m_indexInBlock, SpecNone, Phantom, node->codeOrigin,
+                Edge(edge.node(), StringUse));
+            edge.setUseKind(KnownStringUse);
+            return;
+        }
+        
+        // FIXME: We ought to be able to have a ToPrimitiveToString node.
+        
+        observeUseKindOnNode<useKind>(edge.node());
+        edge = Edge(m_insertionSet.insertNode(
+            m_indexInBlock, SpecString, ToString, node->codeOrigin,
+            Edge(edge.node(), useKind)), KnownStringUse);
+    }
+    
+    template<UseKind leftUseKind>
+    bool attemptToMakeFastStringAdd(Node* node, Edge& left, Edge& right)
+    {
+        ASSERT(leftUseKind == StringUse || leftUseKind == StringObjectUse || leftUseKind == StringOrStringObjectUse);
+        
+        if (isStringObjectUse<leftUseKind>() && !canOptimizeStringObjectAccess(node->codeOrigin))
+            return false;
+        
+        if (right->shouldSpeculateString()) {
+            convertStringAddUse<leftUseKind>(node, left);
+            convertStringAddUse<StringUse>(node, right);
+            return true;
+        }
+        
+        if (right->shouldSpeculateStringObject()
+            && canOptimizeStringObjectAccess(node->codeOrigin)) {
+            convertStringAddUse<leftUseKind>(node, left);
+            convertStringAddUse<StringObjectUse>(node, right);
+            return true;
+        }
+        
+        if (right->shouldSpeculateStringOrStringObject()
+            && canOptimizeStringObjectAccess(node->codeOrigin)) {
+            convertStringAddUse<leftUseKind>(node, left);
+            convertStringAddUse<StringOrStringObjectUse>(node, right);
+            return true;
+        }
+        
+        // FIXME: We ought to be able to convert the right case to do
+        // ToPrimitiveToString.
+        return false; // Let the slow path worry about it.
+    }
+    
     bool isStringPrototypeMethodSane(Structure* stringPrototypeStructure, const Identifier& ident)
     {
         unsigned attributesUnused;