Allow CreateActivation sinking
https://bugs.webkit.org/show_bug.cgi?id=144300

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

This pursues the work started in
https://bugs.webkit.org/show_bug.cgi?id=144016 to expand the set of
allocations we are able to sink by allowing sinking of CreateActivation
node.

This is achieved by following closely the way NewObject is currently
sunk: we add a new PhantomCreateActivation node to record the initial
position of the CreateActivation node, new ClosureVarPLoc promoted heap
locations to keep track of the variables put in the activation, and a
new MaterializeCreateActivation node to allocate and populate the sunk
activation.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.cpp:
(JSC::DFG::Node::convertToPutClosureVarHint):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToPhantomCreateActivation):
(JSC::DFG::Node::isActivationAllocation):
(JSC::DFG::Node::isPhantomActivationAllocation):
(JSC::DFG::Node::isPhantomAllocation):
* dfg/DFGNodeType.h:
* dfg/DFGObjectAllocationSinkingPhase.cpp:
(JSC::DFG::ObjectAllocationSinkingPhase::lowerNonReadingOperationsOnPhantomAllocations):
(JSC::DFG::ObjectAllocationSinkingPhase::handleNode):
(JSC::DFG::ObjectAllocationSinkingPhase::createMaterialize):
(JSC::DFG::ObjectAllocationSinkingPhase::populateMaterialize):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGPromotedHeapLocation.cpp:
(WTF::printInternal):
* dfg/DFGPromotedHeapLocation.h:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGValidate.cpp:
(JSC::DFG::Validate::validateCPS):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileMaterializeCreateActivation):
* ftl/FTLOperations.cpp:
(JSC::FTL::operationMaterializeObjectInOSR):
* tests/stress/activation-sink-osrexit.js: Added.
(bar):
(foo.set result):
* tests/stress/activation-sink.js: Added.
(bar):

LayoutTests:

Add a performance test for activation allocation sinking.

* js/regress/script-tests/sink-huge-activation.js: Added.
(bar):
(foo):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@183812 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp b/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
index 339b7df..19aa2bc 100644
--- a/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
@@ -506,6 +506,15 @@
                     }
                     break;
                 }
+
+                case PutClosureVar: {
+                    Node* target = node->child1().node();
+                    if (m_sinkCandidates.contains(target)) {
+                        ASSERT(target->isPhantomActivationAllocation());
+                        node->convertToPutClosureVarHint();
+                    }
+                    break;
+                }
                     
                 case PutStructure: {
                     Node* target = node->child1().node();
@@ -569,11 +578,43 @@
                     break;
                 }
 
+                case CreateActivation: {
+                    if (m_sinkCandidates.contains(node)) {
+                        m_insertionSet.insert(
+                            nodeIndex + 1,
+                            PromotedHeapLocation(ActivationScopePLoc, node).createHint(
+                                m_graph, node->origin, node->child1().node()));
+                        node->convertToPhantomCreateActivation();
+                    }
+                    break;
+                }
+
+                case MaterializeCreateActivation: {
+                    if (m_sinkCandidates.contains(node)) {
+                        m_insertionSet.insert(
+                            nodeIndex + 1,
+                            PromotedHeapLocation(ActivationScopePLoc, node).createHint(
+                                m_graph, node->origin, m_graph.varArgChild(node, 0).node()));
+                        ObjectMaterializationData& data = node->objectMaterializationData();
+                        for (unsigned i = 0; i < data.m_properties.size(); ++i) {
+                            unsigned identifierNumber = data.m_properties[i].m_identifierNumber;
+                            m_insertionSet.insert(
+                                nodeIndex + 1,
+                                PromotedHeapLocation(
+                                    ClosureVarPLoc, node, identifierNumber).createHint(
+                                    m_graph, node->origin,
+                                    m_graph.varArgChild(node, i + 1).node()));
+                        }
+                        node->convertToPhantomCreateActivation();
+                    }
+                    break;
+                }
+
                 case StoreBarrier:
                 case StoreBarrierWithNullCheck: {
                     Node* target = node->child1().node();
                     if (m_sinkCandidates.contains(target)) {
-                        ASSERT(target->isPhantomObjectAllocation());
+                        ASSERT(target->isPhantomAllocation());
                         node->remove();
                     }
                     break;
@@ -775,6 +816,7 @@
         switch (node->op()) {
         case NewObject:
         case MaterializeNewObject:
+        case MaterializeCreateActivation:
             sinkCandidate();
             m_graph.doToChildren(
                 node,
@@ -793,18 +835,28 @@
                 });
             break;
 
+        case CreateActivation:
+            if (!m_graph.symbolTableFor(node->origin.semantic)->singletonScope()->isStillValid())
+                sinkCandidate();
+            m_graph.doToChildren(
+                node,
+                [&] (Edge edge) {
+                    escape(edge.node());
+                });
+            break;
+
         case MovHint:
         case Check:
         case PutHint:
+        case StoreBarrier:
+        case StoreBarrierWithNullCheck:
             break;
 
         case PutStructure:
         case CheckStructure:
         case GetByOffset:
         case MultiGetByOffset:
-        case GetGetterSetterByOffset:
-        case StoreBarrier:
-        case StoreBarrierWithNullCheck: {
+        case GetGetterSetterByOffset: {
             Node* target = node->child1().node();
             if (!target->isObjectAllocation())
                 escape(target);
@@ -820,6 +872,14 @@
             escape(node->child3().node());
             break;
         }
+
+        case PutClosureVar: {
+            Node* target = node->child1().node();
+            if (!target->isActivationAllocation())
+                escape(target);
+            escape(node->child2().node());
+            break;
+        }
             
         case MultiPutByOffset:
             // FIXME: In the future we should be able to handle this. It's just a matter of
@@ -869,6 +929,19 @@
                 escapee->child1());
             break;
 
+        case CreateActivation:
+        case MaterializeCreateActivation: {
+            ObjectMaterializationData* data = m_graph.m_objectMaterializationData.add();
+
+            result = m_graph.addNode(
+                escapee->prediction(), Node::VarArg, MaterializeCreateActivation,
+                NodeOrigin(
+                    escapee->origin.semantic,
+                    where->origin.forExit),
+                OpInfo(data), OpInfo(), 0, 0);
+            break;
+        }
+
         default:
             DFG_CRASH(m_graph, escapee, "Bad escapee op");
             break;
@@ -928,6 +1001,46 @@
             break;
         }
 
+        case MaterializeCreateActivation: {
+            ObjectMaterializationData& data = node->objectMaterializationData();
+
+            unsigned firstChild = m_graph.m_varArgChildren.size();
+
+            Vector<PromotedHeapLocation> locations = m_locationsForAllocation.get(escapee);
+
+            PromotedHeapLocation scope(ActivationScopePLoc, escapee);
+            ASSERT(locations.contains(scope));
+
+            m_graph.m_varArgChildren.append(Edge(resolve(block, scope), KnownCellUse));
+
+            for (unsigned i = 0; i < locations.size(); ++i) {
+                switch (locations[i].kind()) {
+                case ActivationScopePLoc: {
+                    ASSERT(locations[i] == scope);
+                    break;
+                }
+
+                case ClosureVarPLoc: {
+                    Node* value = resolve(block, locations[i]);
+                    if (value->op() == BottomValue)
+                        break;
+
+                    data.m_properties.append(PhantomPropertyValue(locations[i].info()));
+                    m_graph.m_varArgChildren.append(value);
+                    break;
+                }
+
+                default:
+                    DFG_CRASH(m_graph, node, "Bad location kind");
+                }
+            }
+
+            node->children = AdjacencyList(
+                AdjacencyList::Variable,
+                firstChild, m_graph.m_varArgChildren.size() - firstChild);
+            break;
+        }
+
         case NewFunction: {
             if (!ASSERT_DISABLED) {
                 Vector<PromotedHeapLocation> locations = m_locationsForAllocation.get(escapee);