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;