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/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index 9342093..53c845f 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -1495,6 +1495,7 @@
         
     case PhantomNewObject:
     case PhantomNewFunction:
+    case PhantomCreateActivation:
     case PhantomDirectArguments:
     case PhantomClonedArguments:
     case BottomValue:
@@ -1517,8 +1518,9 @@
         forNode(node).set(m_graph, set);
         break;
     }
-        
+
     case CreateActivation:
+    case MaterializeCreateActivation:
         forNode(node).set(
             m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->activationStructure());
         break;
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index e87c38a..60a8994 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -854,6 +854,8 @@
     case PhantomNewObject:
     case MaterializeNewObject:
     case PhantomNewFunction:
+    case PhantomCreateActivation:
+    case MaterializeCreateActivation:
         read(HeapObjectCount);
         write(HeapObjectCount);
         return;
diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
index 4871f0a..a9e30ac 100644
--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
@@ -198,6 +198,7 @@
     case BottomValue:
     case PhantomNewObject:
     case PhantomNewFunction:
+    case PhantomCreateActivation:
     case PhantomDirectArguments:
     case PhantomClonedArguments:
     case GetMyArgumentByVal:
@@ -236,6 +237,7 @@
     case GetEnumeratorGenericPname:
     case ToIndexString:
     case MaterializeNewObject:
+    case MaterializeCreateActivation:
         return true;
         
     case MultiPutByOffset:
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 15dcd41..9bea6c8 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -1029,6 +1029,7 @@
         case BooleanToNumber:
         case PhantomNewObject:
         case PhantomNewFunction:
+        case PhantomCreateActivation:
         case PhantomDirectArguments:
         case PhantomClonedArguments:
         case ForwardVarargs:
@@ -1036,6 +1037,7 @@
         case PutHint:
         case CheckStructureImmediate:
         case MaterializeNewObject:
+        case MaterializeCreateActivation:
         case PutStack:
         case KillStack:
         case GetStack:
diff --git a/Source/JavaScriptCore/dfg/DFGNode.cpp b/Source/JavaScriptCore/dfg/DFGNode.cpp
index b5334c2..5a7cebe 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.cpp
+++ b/Source/JavaScriptCore/dfg/DFGNode.cpp
@@ -158,6 +158,14 @@
         child2().node(), child3().node());
 }
 
+void Node::convertToPutClosureVarHint()
+{
+    ASSERT(m_op == PutClosureVar);
+    convertToPutHint(
+        PromotedLocationDescriptor(ClosureVarPLoc, scopeOffset().offset()),
+        child1().node(), child2().node());
+}
+
 PromotedLocationDescriptor Node::promotedLocationDescriptor()
 {
     return PromotedLocationDescriptor(static_cast<PromotedLocationKind>(m_opInfo), m_opInfo2);
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index 8e67df2..48b68b0 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -569,6 +569,7 @@
     
     void convertToPutByOffsetHint();
     void convertToPutStructureHint(Node* structure);
+    void convertToPutClosureVarHint();
     
     void convertToPhantomNewObject()
     {
@@ -591,6 +592,17 @@
         children = AdjacencyList();
     }
 
+    void convertToPhantomCreateActivation()
+    {
+        ASSERT(m_op == CreateActivation || m_op == MaterializeCreateActivation);
+        m_op = PhantomCreateActivation;
+        m_flags &= ~NodeHasVarArgs;
+        m_flags |= NodeMustGenerate;
+        m_opInfo = 0;
+        m_opInfo2 = 0;
+        children = AdjacencyList();
+    }
+
     void convertPhantomToPhantomLocal()
     {
         ASSERT(m_op == Phantom && (child1()->op() == Phi || child1()->op() == SetLocal || child1()->op() == SetArgument));
@@ -1442,6 +1454,27 @@
         }
     }
     
+    bool isActivationAllocation()
+    {
+        switch (op()) {
+        case CreateActivation:
+        case MaterializeCreateActivation:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    bool isPhantomActivationAllocation()
+    {
+        switch (op()) {
+        case PhantomCreateActivation:
+            return true;
+        default:
+            return false;
+        }
+    }
+
     bool isPhantomAllocation()
     {
         switch (op()) {
@@ -1449,6 +1482,7 @@
         case PhantomDirectArguments:
         case PhantomClonedArguments:
         case PhantomNewFunction:
+        case PhantomCreateActivation:
             return true;
         default:
             return false;
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index b9f025f..75c8842 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -254,6 +254,8 @@
     macro(CheckStructureImmediate, NodeMustGenerate) \
     macro(MaterializeNewObject, NodeResultJS | NodeHasVarArgs) \
     macro(PhantomNewFunction, NodeResultJS | NodeMustGenerate) \
+    macro(PhantomCreateActivation, NodeResultJS | NodeMustGenerate) \
+    macro(MaterializeCreateActivation, NodeResultJS | NodeHasVarArgs) \
     \
     /* Nodes for misc operations. */\
     macro(Breakpoint, NodeMustGenerate) \
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);
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index fdb8fda..31d66ab 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -543,6 +543,7 @@
         case BooleanToNumber:
         case PhantomNewObject:
         case PhantomNewFunction:
+        case PhantomCreateActivation:
         case PhantomDirectArguments:
         case PhantomClonedArguments:
         case GetMyArgumentByVal:
@@ -550,6 +551,7 @@
         case PutHint:
         case CheckStructureImmediate:
         case MaterializeNewObject:
+        case MaterializeCreateActivation:
         case PutStack:
         case KillStack:
         case GetStack: {
diff --git a/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.cpp b/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.cpp
index 771567c..59854f8 100644
--- a/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.cpp
@@ -90,6 +90,14 @@
     case FunctionActivationPLoc:
         out.print("FunctionActivationPLoc");
         return;
+
+    case ActivationScopePLoc:
+        out.print("ActivationScopePLoc");
+        return;
+
+    case ClosureVarPLoc:
+        out.print("ClosureVarPLoc");
+        return;
     }
     
     RELEASE_ASSERT_NOT_REACHED();
diff --git a/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h b/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h
index 51f7b9e..f18c508 100644
--- a/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h
+++ b/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h
@@ -44,6 +44,8 @@
 
     FunctionExecutablePLoc,
     FunctionActivationPLoc,
+    ActivationScopePLoc,
+    ClosureVarPLoc,
 };
 
 class PromotedLocationDescriptor {
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index aa5a71c..f4b079f 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -273,9 +273,11 @@
     case ToIndexString:
     case PhantomNewObject:
     case PhantomNewFunction:
+    case PhantomCreateActivation:
     case PutHint:
     case CheckStructureImmediate:
     case MaterializeNewObject:
+    case MaterializeCreateActivation:
     case PhantomDirectArguments:
     case PhantomClonedArguments:
     case GetMyArgumentByVal:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 745c9a8..23cf244 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -4595,9 +4595,11 @@
     case BottomValue:
     case PhantomNewObject:
     case PhantomNewFunction:
+    case PhantomCreateActivation:
     case PutHint:
     case CheckStructureImmediate:
     case MaterializeNewObject:
+    case MaterializeCreateActivation:
     case PutStack:
     case KillStack:
     case GetStack:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 9e9b1c9..3e574d4 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -4691,10 +4691,12 @@
     case BottomValue:
     case PhantomNewObject:
     case PhantomNewFunction:
+    case PhantomCreateActivation:
     case GetMyArgumentByVal:
     case PutHint:
     case CheckStructureImmediate:
     case MaterializeNewObject:
+    case MaterializeCreateActivation:
     case PutStack:
     case KillStack:
     case GetStack:
diff --git a/Source/JavaScriptCore/dfg/DFGValidate.cpp b/Source/JavaScriptCore/dfg/DFGValidate.cpp
index d46dace..f892453 100644
--- a/Source/JavaScriptCore/dfg/DFGValidate.cpp
+++ b/Source/JavaScriptCore/dfg/DFGValidate.cpp
@@ -424,10 +424,12 @@
                 case CheckInBounds:
                 case PhantomNewObject:
                 case PhantomNewFunction:
+                case PhantomCreateActivation:
                 case GetMyArgumentByVal:
                 case PutHint:
                 case CheckStructureImmediate:
                 case MaterializeNewObject:
+                case MaterializeCreateActivation:
                 case PutStack:
                 case KillStack:
                 case GetStack: