DFG string conversions and allocations should be inlined
https://bugs.webkit.org/show_bug.cgi?id=112376

Source/JavaScriptCore: 

Reviewed by Geoffrey Garen.
        
This turns new String(), String(), String.prototype.valueOf(), and
String.prototype.toString() into intrinsics. It gives the DFG the ability to handle
conversions from StringObject to JSString and vice-versa, and also gives it the
ability to handle cases where a variable may be either a StringObject or a JSString.
To do this, I added StringObject to value profiling (and removed the stale
distinction between Myarguments and Foreignarguments). I also cleaned up ToPrimitive
handling, using some of the new functionality but also taking advantage of the
existence of Identity(String:@a).
        
This is a 2% SunSpider speed-up. Also there are some speed-ups on V8v7 and Kraken.
On microbenchmarks that stress new String() this is a 14x speed-up.

* CMakeLists.txt:
* DerivedSources.make:
* DerivedSources.pri:
* GNUmakefile.list.am:
* bytecode/CodeBlock.h:
(CodeBlock):
(JSC::CodeBlock::hasExitSite):
(JSC):
* bytecode/DFGExitProfile.cpp:
(JSC::DFG::ExitProfile::hasExitSite):
(DFG):
* bytecode/DFGExitProfile.h:
(ExitProfile):
(JSC::DFG::ExitProfile::hasExitSite):
* bytecode/ExitKind.cpp:
(JSC::exitKindToString):
* bytecode/ExitKind.h:
* bytecode/SpeculatedType.cpp:
(JSC::dumpSpeculation):
(JSC::speculationToAbbreviatedString):
(JSC::speculationFromClassInfo):
* bytecode/SpeculatedType.h:
(JSC):
(JSC::isStringObjectSpeculation):
(JSC::isStringOrStringObjectSpeculation):
* create_hash_table:
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::executeEffects):
* dfg/DFGAbstractState.h:
(JSC::DFG::AbstractState::filterEdgeByUse):
* dfg/DFGByteCodeParser.cpp:
(ByteCodeParser):
(JSC::DFG::ByteCodeParser::handleCall):
(JSC::DFG::ByteCodeParser::emitArgumentPhantoms):
(DFG):
(JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::putStructureStoreElimination):
* dfg/DFGEdge.h:
(JSC::DFG::Edge::shift):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::isStringPrototypeMethodSane):
(FixupPhase):
(JSC::DFG::FixupPhase::canOptimizeStringObjectAccess):
(JSC::DFG::FixupPhase::observeUseKindOnNode):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::hasGlobalExitSite):
(Graph):
(JSC::DFG::Graph::hasExitSite):
(JSC::DFG::Graph::clobbersWorld):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToToString):
(Node):
(JSC::DFG::Node::hasStructure):
(JSC::DFG::Node::shouldSpeculateStringObject):
(JSC::DFG::Node::shouldSpeculateStringOrStringObject):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileToStringOnCell):
(DFG):
(JSC::DFG::SpeculativeJIT::compileNewStringObject):
(JSC::DFG::SpeculativeJIT::speculateObject):
(JSC::DFG::SpeculativeJIT::speculateObjectOrOther):
(JSC::DFG::SpeculativeJIT::speculateString):
(JSC::DFG::SpeculativeJIT::speculateStringObject):
(JSC::DFG::SpeculativeJIT::speculateStringOrStringObject):
(JSC::DFG::SpeculativeJIT::speculate):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
(SpeculativeJIT):
(JSC::DFG::SpeculateCellOperand::SpeculateCellOperand):
(DFG):
(JSC::DFG::SpeculativeJIT::speculateStringObjectForStructure):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::fillSpeculateCell):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::fillSpeculateCell):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGUseKind.cpp:
(WTF::printInternal):
* dfg/DFGUseKind.h:
(JSC::DFG::typeFilterFor):
* interpreter/CallFrame.h:
(JSC::ExecState::regExpPrototypeTable):
* runtime/CommonIdentifiers.h:
* runtime/Intrinsic.h:
* runtime/JSDestructibleObject.h:
(JSDestructibleObject):
(JSC::JSDestructibleObject::classInfoOffset):
* runtime/JSGlobalData.cpp:
(JSC):
(JSC::JSGlobalData::JSGlobalData):
(JSC::JSGlobalData::~JSGlobalData):
* runtime/JSGlobalData.h:
(JSGlobalData):
* runtime/JSObject.cpp:
* runtime/JSObject.h:
(JSC):
* runtime/JSWrapperObject.h:
(JSC::JSWrapperObject::allocationSize):
(JSWrapperObject):
(JSC::JSWrapperObject::internalValueOffset):
(JSC::JSWrapperObject::internalValueCellOffset):
* runtime/StringPrototype.cpp:
(JSC):
(JSC::StringPrototype::finishCreation):
(JSC::StringPrototype::create):
* runtime/StringPrototype.h:
(StringPrototype):

LayoutTests: 

Reviewed by Geoffrey Garen.

* fast/js/dfg-to-string-bad-toString-expected.txt: Added.
* fast/js/dfg-to-string-bad-toString.html: Added.
* fast/js/dfg-to-string-bad-valueOf-expected.txt: Added.
* fast/js/dfg-to-string-bad-valueOf.html: Added.
* fast/js/dfg-to-string-int-expected.txt: Added.
* fast/js/dfg-to-string-int-or-string-expected.txt: Added.
* fast/js/dfg-to-string-int-or-string.html: Added.
* fast/js/dfg-to-string-int.html: Added.
* fast/js/dfg-to-string-side-effect-clobbers-toString-expected.txt: Added.
* fast/js/dfg-to-string-side-effect-clobbers-toString.html: Added.
* fast/js/dfg-to-string-side-effect-expected.txt: Added.
* fast/js/dfg-to-string-side-effect.html: Added.
* fast/js/dfg-to-string-toString-becomes-bad-expected.txt: Added.
* fast/js/dfg-to-string-toString-becomes-bad-with-dictionary-string-prototype-expected.txt: Added.
* fast/js/dfg-to-string-toString-becomes-bad-with-dictionary-string-prototype.html: Added.
* fast/js/dfg-to-string-toString-becomes-bad.html: Added.
* fast/js/dfg-to-string-toString-in-string-expected.txt: Added.
* fast/js/dfg-to-string-toString-in-string.html: Added.
* fast/js/dfg-to-string-valueOf-becomes-bad-expected.txt: Added.
* fast/js/dfg-to-string-valueOf-becomes-bad.html: Added.
* fast/js/dfg-to-string-valueOf-in-string-expected.txt: Added.
* fast/js/dfg-to-string-valueOf-in-string.html: Added.
* fast/js/jsc-test-list:
* fast/js/regress/script-tests/string-concat-object.js: Added.
(foo):
* fast/js/regress/script-tests/string-concat-pair-object.js: Added.
(foo):
* fast/js/regress/script-tests/string-concat-pair-simple.js: Added.
(foo):
* fast/js/regress/script-tests/string-concat-simple.js: Added.
(foo):
* fast/js/regress/script-tests/string-cons-repeat.js: Added.
(foo):
* fast/js/regress/script-tests/string-cons-tower.js: Added.
(foo):
* fast/js/regress/string-concat-object-expected.txt: Added.
* fast/js/regress/string-concat-object.html: Added.
* fast/js/regress/string-concat-pair-object-expected.txt: Added.
* fast/js/regress/string-concat-pair-object.html: Added.
* fast/js/regress/string-concat-pair-simple-expected.txt: Added.
* fast/js/regress/string-concat-pair-simple.html: Added.
* fast/js/regress/string-concat-simple-expected.txt: Added.
* fast/js/regress/string-concat-simple.html: Added.
* fast/js/regress/string-cons-repeat-expected.txt: Added.
* fast/js/regress/string-cons-repeat.html: Added.
* fast/js/regress/string-cons-tower-expected.txt: Added.
* fast/js/regress/string-cons-tower.html: Added.
* fast/js/script-tests/dfg-to-string-bad-toString.js: Added.
(String.prototype.toString):
(foo):
* fast/js/script-tests/dfg-to-string-bad-valueOf.js: Added.
(String.prototype.valueOf):
(foo):
* fast/js/script-tests/dfg-to-string-int-or-string.js: Added.
(foo):
* fast/js/script-tests/dfg-to-string-int.js: Added.
(foo):
* fast/js/script-tests/dfg-to-string-side-effect-clobbers-toString.js: Added.
(foo):
* fast/js/script-tests/dfg-to-string-side-effect.js: Added.
(foo):
* fast/js/script-tests/dfg-to-string-toString-becomes-bad-with-dictionary-string-prototype.js: Added.
(foo):
(.String.prototype.toString):
* fast/js/script-tests/dfg-to-string-toString-becomes-bad.js: Added.
(foo):
(.String.prototype.toString):
* fast/js/script-tests/dfg-to-string-toString-in-string.js: Added.
(foo):
(.argument.toString):
* fast/js/script-tests/dfg-to-string-valueOf-becomes-bad.js: Added.
(foo):
(.String.prototype.valueOf):
* fast/js/script-tests/dfg-to-string-valueOf-in-string.js: Added.
(foo):
(.argument.valueOf):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@146089 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 4b0bbf3..6e8d922 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -526,13 +526,69 @@
         }
             
         case ToPrimitive: {
-            if (node->child1()->shouldSpeculateInteger())
+            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
             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;
+            }
+            
+            break;
+        }
+            
+        case NewStringObject: {
+            setUseKindAndUnboxIfProfitable<KnownStringUse>(node->child1());
+            break;
+        }
+            
         case NewArray: {
             for (unsigned i = m_graph.varArgNumChildren(node); i--;) {
                 node->setIndexingType(
@@ -806,6 +862,58 @@
 #endif
     }
     
+    bool isStringPrototypeMethodSane(Structure* stringPrototypeStructure, const Identifier& ident)
+    {
+        unsigned attributesUnused;
+        JSCell* specificValue;
+        PropertyOffset offset = stringPrototypeStructure->get(
+            globalData(), ident, attributesUnused, specificValue);
+        if (!isValidOffset(offset))
+            return false;
+        
+        if (!specificValue)
+            return false;
+        
+        if (!specificValue->inherits(&JSFunction::s_info))
+            return false;
+        
+        JSFunction* function = jsCast<JSFunction*>(specificValue);
+        if (function->executable()->intrinsicFor(CodeForCall) != StringPrototypeValueOfIntrinsic)
+            return false;
+        
+        return true;
+    }
+    
+    bool canOptimizeStringObjectAccess(const CodeOrigin& codeOrigin)
+    {
+        if (m_graph.hasExitSite(codeOrigin, NotStringObject))
+            return false;
+        
+        Structure* stringObjectStructure = m_graph.globalObjectFor(codeOrigin)->stringObjectStructure();
+        ASSERT(stringObjectStructure->storedPrototype().isObject());
+        ASSERT(stringObjectStructure->storedPrototype().asCell()->classInfo() == &StringPrototype::s_info);
+        
+        JSObject* stringPrototypeObject = asObject(stringObjectStructure->storedPrototype());
+        Structure* stringPrototypeStructure = stringPrototypeObject->structure();
+        if (stringPrototypeStructure->transitionWatchpointSetHasBeenInvalidated())
+            return false;
+        
+        if (stringPrototypeStructure->isDictionary())
+            return false;
+        
+        // We're being conservative here. We want DFG's ToString on StringObject to be
+        // used in both numeric contexts (that would call valueOf()) and string contexts
+        // (that would call toString()). We don't want the DFG to have to distinguish
+        // between the two, just because that seems like it would get confusing. So we
+        // just require both methods to be sane.
+        if (!isStringPrototypeMethodSane(stringPrototypeStructure, globalData().propertyNames->valueOf))
+            return false;
+        if (!isStringPrototypeMethodSane(stringPrototypeStructure, globalData().propertyNames->toString))
+            return false;
+        
+        return true;
+    }
+    
     void fixupSetLocalsInBlock(BasicBlock* block)
     {
         if (!block)
@@ -974,6 +1082,9 @@
         case CellUse:
         case ObjectUse:
         case StringUse:
+        case KnownStringUse:
+        case StringObjectUse:
+        case StringOrStringObjectUse:
             if (alwaysUnboxSimplePrimitives()
                 || isCellSpeculation(variable->prediction()))
                 m_profitabilityChanged |= variable->mergeIsProfitableToUnbox(true);