Title: [180160] trunk/Source/_javascript_Core
Revision
180160
Author
fpi...@apple.com
Date
2015-02-16 11:27:37 -0800 (Mon, 16 Feb 2015)

Log Message

DFG SSA should use GetLocal for arguments, and the GetArgument node type should be removed
https://bugs.webkit.org/show_bug.cgi?id=141623

Reviewed by Oliver Hunt.
        
During development of https://bugs.webkit.org/show_bug.cgi?id=141332, I realized that I
needed to use GetArgument for loading something that has magically already appeared on the
stack, so currently trunk sort of allows this. But then I realized three things:
        
- A GetArgument with a non-JSValue flush format means speculating that the value on the
  stack obeys that format, rather than just assuming that that it already has that format.
  In bug 141332, I want it to assume rather than speculate. That also happens to be more
  intuitive; I don't think I was wrong to expect that.
        
- The node I really want is GetLocal. I'm just getting the value of the local and I don't
  want to do anything else.
        
- Maybe it would be easier if we just used GetLocal for all of the cases where we currently
  use GetArgument.
        
This changes the FTL to do argument speculations in the prologue just like the DFG does.
This brings some consistency to our system, and allows us to get rid of the GetArgument
node. The speculations that the FTL must do are now made explicit in the m_argumentFormats
vector in DFG::Graph. This has natural DCE behavior: even if all uses of the argument are
dead we will still speculate. We already have safeguards to ensure we only speculate if
there are uses that benefit from speculation (which is a much more conservative criterion
than DCE).
        
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDCEPhase.cpp:
(JSC::DFG::DCEPhase::run):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGFlushFormat.h:
(JSC::DFG::typeFilterFor):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::valueProfileFor):
(JSC::DFG::Graph::methodOfGettingAValueProfileFor):
* dfg/DFGInPlaceAbstractState.cpp:
(JSC::DFG::InPlaceAbstractState::initialize):
* dfg/DFGNode.cpp:
(JSC::DFG::Node::hasVariableAccessData):
* dfg/DFGNodeType.h:
* dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
(JSC::DFG::OSRAvailabilityAnalysisPhase::run):
(JSC::DFG::LocalOSRAvailabilityCalculator::executeNode):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGPutLocalSinkingPhase.cpp:
* dfg/DFGSSAConversionPhase.cpp:
(JSC::DFG::SSAConversionPhase::run):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::lower):
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileGetLocal):
(JSC::FTL::LowerDFGToLLVM::compileGetArgument): Deleted.
* tests/stress/dead-speculating-argument-use.js: Added.
(foo):
(o.valueOf):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (180159 => 180160)


--- trunk/Source/_javascript_Core/ChangeLog	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-02-16 19:27:37 UTC (rev 180160)
@@ -1,3 +1,80 @@
+2015-02-16  Filip Pizlo  <fpi...@apple.com>
+
+        DFG SSA should use GetLocal for arguments, and the GetArgument node type should be removed
+        https://bugs.webkit.org/show_bug.cgi?id=141623
+
+        Reviewed by Oliver Hunt.
+        
+        During development of https://bugs.webkit.org/show_bug.cgi?id=141332, I realized that I
+        needed to use GetArgument for loading something that has magically already appeared on the
+        stack, so currently trunk sort of allows this. But then I realized three things:
+        
+        - A GetArgument with a non-JSValue flush format means speculating that the value on the
+          stack obeys that format, rather than just assuming that that it already has that format.
+          In bug 141332, I want it to assume rather than speculate. That also happens to be more
+          intuitive; I don't think I was wrong to expect that.
+        
+        - The node I really want is GetLocal. I'm just getting the value of the local and I don't
+          want to do anything else.
+        
+        - Maybe it would be easier if we just used GetLocal for all of the cases where we currently
+          use GetArgument.
+        
+        This changes the FTL to do argument speculations in the prologue just like the DFG does.
+        This brings some consistency to our system, and allows us to get rid of the GetArgument
+        node. The speculations that the FTL must do are now made explicit in the m_argumentFormats
+        vector in DFG::Graph. This has natural DCE behavior: even if all uses of the argument are
+        dead we will still speculate. We already have safeguards to ensure we only speculate if
+        there are uses that benefit from speculation (which is a much more conservative criterion
+        than DCE).
+        
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDCEPhase.cpp:
+        (JSC::DFG::DCEPhase::run):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGFlushFormat.h:
+        (JSC::DFG::typeFilterFor):
+        * dfg/DFGGraph.cpp:
+        (JSC::DFG::Graph::dump):
+        * dfg/DFGGraph.h:
+        (JSC::DFG::Graph::valueProfileFor):
+        (JSC::DFG::Graph::methodOfGettingAValueProfileFor):
+        * dfg/DFGInPlaceAbstractState.cpp:
+        (JSC::DFG::InPlaceAbstractState::initialize):
+        * dfg/DFGNode.cpp:
+        (JSC::DFG::Node::hasVariableAccessData):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
+        (JSC::DFG::OSRAvailabilityAnalysisPhase::run):
+        (JSC::DFG::LocalOSRAvailabilityCalculator::executeNode):
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGPutLocalSinkingPhase.cpp:
+        * dfg/DFGSSAConversionPhase.cpp:
+        (JSC::DFG::SSAConversionPhase::run):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::LowerDFGToLLVM::lower):
+        (JSC::FTL::LowerDFGToLLVM::compileNode):
+        (JSC::FTL::LowerDFGToLLVM::compileGetLocal):
+        (JSC::FTL::LowerDFGToLLVM::compileGetArgument): Deleted.
+        * tests/stress/dead-speculating-argument-use.js: Added.
+        (foo):
+        (o.valueOf):
+
 2015-02-15  Filip Pizlo  <fpi...@apple.com>
 
         Rare case profiling should actually work

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2015-02-16 19:27:37 UTC (rev 180160)
@@ -141,18 +141,6 @@
         break;
     }
         
-    case GetArgument: {
-        ASSERT(m_graph.m_form == SSA);
-        VariableAccessData* variable = node->variableAccessData();
-        AbstractValue& value = m_state.variables().operand(variable->local().offset());
-        ASSERT(value.isHeapTop());
-        FiltrationResult result =
-            value.filter(typeFilterFor(useKindFor(variable->flushFormat())));
-        ASSERT_UNUSED(result, result == FiltrationOK);
-        forNode(node) = value;
-        break;
-    }
-        
     case ExtractOSREntryLocal: {
         if (!(node->unlinkedLocal().isArgument())
             && m_graph.m_lazyVars.get(node->unlinkedLocal().toLocal())) {
@@ -170,6 +158,8 @@
     case GetLocal: {
         VariableAccessData* variableAccessData = node->variableAccessData();
         AbstractValue value = m_state.variables().operand(variableAccessData->local().offset());
+        // The value in the local should already be checked.
+        DFG_ASSERT(m_graph, node, value.isType(typeFilterFor(variableAccessData->flushFormat())));
         if (value.value())
             m_state.setFoundConstants(true);
         forNode(node) = value;

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2015-02-16 19:27:37 UTC (rev 180160)
@@ -391,7 +391,6 @@
         return;
         
     case GetLocal:
-    case GetArgument:
         read(AbstractHeap(Variables, node->local()));
         def(HeapLocation(VariableLoc, AbstractHeap(Variables, node->local())), node);
         return;

Modified: trunk/Source/_javascript_Core/dfg/DFGDCEPhase.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGDCEPhase.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGDCEPhase.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -53,12 +53,21 @@
         if (m_graph.m_form == SSA) {
             for (BasicBlock* block : m_graph.blocksInPreOrder())
                 fixupBlock(block);
+            
+            // This is like cleanVariables, but has a much simpler approach to GetLocal.
+            for (unsigned i = m_graph.m_arguments.size(); i--;) {
+                Node* node = m_graph.m_arguments[i];
+                if (!node)
+                    continue;
+                if (node->op() != Phantom && node->op() != Check && node->shouldGenerate())
+                    continue;
+                m_graph.m_arguments[i] = nullptr;
+            }
         } else {
             RELEASE_ASSERT(m_graph.m_form == ThreadedCPS);
             
             for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex)
                 fixupBlock(m_graph.block(blockIndex));
-            
             cleanVariables(m_graph.m_arguments);
         }
         

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -53,7 +53,6 @@
     case SetLocal:
     case MovHint:
     case ZombieHint:
-    case GetArgument:
     case Phantom:
     case HardPhantom:
     case Upsilon:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -1033,7 +1033,6 @@
         case GetArrayLength:
         case Phi:
         case Upsilon:
-        case GetArgument:
         case GetIndexedPropertyStorage:
         case GetTypedArrayByteOffset:
         case LastNodeType:

Modified: trunk/Source/_javascript_Core/dfg/DFGFlushFormat.h (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGFlushFormat.h	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGFlushFormat.h	2015-02-16 19:27:37 UTC (rev 180160)
@@ -93,6 +93,11 @@
     return UntypedUse;
 }
 
+inline SpeculatedType typeFilterFor(FlushFormat format)
+{
+    return typeFilterFor(useKindFor(format));
+}
+
 inline DataFormat dataFormatFor(FlushFormat format)
 {
     switch (format) {

Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGGraph.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -425,6 +425,10 @@
     out.print("\n");
     out.print("DFG for ", CodeBlockWithJITType(m_codeBlock, JITCode::DFGJIT), ":\n");
     out.print("  Fixpoint state: ", m_fixpointState, "; Form: ", m_form, "; Unification state: ", m_unificationState, "; Ref count state: ", m_refCountState, "\n");
+    if (m_form == SSA)
+        out.print("  Argument formats: ", listDump(m_argumentFormats), "\n");
+    else
+        out.print("  Arguments: ", listDump(m_arguments), "\n");
     out.print("\n");
     
     Node* lastNode = 0;

Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.h (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGGraph.h	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h	2015-02-16 19:27:37 UTC (rev 180160)
@@ -475,21 +475,19 @@
     ValueProfile* valueProfileFor(Node* node)
     {
         if (!node)
-            return 0;
+            return nullptr;
         
         CodeBlock* profiledBlock = baselineCodeBlockFor(node->origin.semantic);
         
-        if (node->op() == GetArgument)
-            return profiledBlock->valueProfileForArgument(node->local().toArgument());
-        
         if (node->hasLocal(*this)) {
-            if (m_form == SSA)
-                return 0;
             if (!node->local().isArgument())
                 return 0;
             int argument = node->local().toArgument();
-            if (node->variableAccessData() != m_arguments[argument]->variableAccessData())
-                return 0;
+            Node* argumentNode = m_arguments[argument];
+            if (!argumentNode)
+                return nullptr;
+            if (node->variableAccessData() != argumentNode->variableAccessData())
+                return nullptr;
             return profiledBlock->valueProfileForArgument(argument);
         }
         
@@ -504,16 +502,19 @@
         if (!node)
             return MethodOfGettingAValueProfile();
         
-        CodeBlock* profiledBlock = baselineCodeBlockFor(node->origin.semantic);
+        if (ValueProfile* valueProfile = valueProfileFor(node))
+            return MethodOfGettingAValueProfile(valueProfile);
         
         if (node->op() == GetLocal) {
+            CodeBlock* profiledBlock = baselineCodeBlockFor(node->origin.semantic);
+        
             return MethodOfGettingAValueProfile::fromLazyOperand(
                 profiledBlock,
                 LazyOperandValueProfileKey(
                     node->origin.semantic.bytecodeIndex, node->local()));
         }
         
-        return MethodOfGettingAValueProfile(valueProfileFor(node));
+        return MethodOfGettingAValueProfile();
     }
     
     bool usesArguments() const
@@ -811,7 +812,37 @@
     Bag<FrozenValue> m_frozenValues;
     
     Bag<StorageAccessData> m_storageAccessData;
+    
+    // In CPS, this is all of the SetArgument nodes for the arguments in the machine code block
+    // that survived DCE. All of them except maybe "this" will survive DCE, because of the Flush
+    // nodes.
+    //
+    // In SSA, this is all of the GetLocal nodes for the arguments in the machine code block that
+    // may have some speculation in the prologue and survived DCE. Note that to get the speculation
+    // for an argument in SSA, you must use m_argumentFormats, since we still have to speculate
+    // even if the argument got killed. For example:
+    //
+    //     function foo(x) {
+    //        var tmp = x + 1;
+    //     }
+    //
+    // Assume that x is always int during profiling. The ArithAdd for "x + 1" will be dead and will
+    // have a proven check for the edge to "x". So, we will not insert a Check node and we will
+    // kill the GetLocal for "x". But, we must do the int check in the progolue, because that's the
+    // thing we used to allow DCE of ArithAdd. Otherwise the add could be impure:
+    //
+    //     var o = {
+    //         valueOf: function() { do side effects; }
+    //     };
+    //     foo(o);
+    //
+    // If we DCE the ArithAdd and we remove the int check on x, then this won't do the side
+    // effects.
     Vector<Node*, 8> m_arguments;
+    
+    // In CPS, this is meaningless. In SSA, this is the argument speculation that we've locked in.
+    Vector<FlushFormat> m_argumentFormats;
+    
     SegmentedVector<VariableAccessData, 16> m_variableAccessData;
     SegmentedVector<ArgumentPosition, 8> m_argumentPositions;
     SegmentedVector<StructureSet, 16> m_structureSet;

Modified: trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -96,28 +96,37 @@
     root->cfaStructureClobberStateAtTail = StructuresAreWatched;
     for (size_t i = 0; i < root->valuesAtHead.numberOfArguments(); ++i) {
         root->valuesAtTail.argument(i).clear();
-        if (m_graph.m_form == SSA) {
-            root->valuesAtHead.argument(i).makeHeapTop();
-            continue;
+
+        FlushFormat format;
+        if (m_graph.m_form == SSA)
+            format = m_graph.m_argumentFormats[i];
+        else {
+            Node* node = m_graph.m_arguments[i];
+            if (!node)
+                format = FlushedJSValue;
+            else {
+                ASSERT(node->op() == SetArgument);
+                format = node->variableAccessData()->flushFormat();
+            }
         }
         
-        Node* node = root->variablesAtHead.argument(i);
-        ASSERT(node->op() == SetArgument);
-        if (!node->variableAccessData()->shouldUnboxIfPossible()) {
-            root->valuesAtHead.argument(i).makeHeapTop();
-            continue;
-        }
-        
-        SpeculatedType prediction =
-            node->variableAccessData()->argumentAwarePrediction();
-        if (isInt32Speculation(prediction))
+        switch (format) {
+        case FlushedInt32:
             root->valuesAtHead.argument(i).setType(SpecInt32);
-        else if (isBooleanSpeculation(prediction))
+            break;
+        case FlushedBoolean:
             root->valuesAtHead.argument(i).setType(SpecBoolean);
-        else if (isCellSpeculation(prediction))
+            break;
+        case FlushedCell:
             root->valuesAtHead.argument(i).setType(SpecCell);
-        else
+            break;
+        case FlushedJSValue:
             root->valuesAtHead.argument(i).makeHeapTop();
+            break;
+        default:
+            DFG_CRASH(m_graph, nullptr, "Bad flush format for argument");
+            break;
+        }
     }
     for (size_t i = 0; i < root->valuesAtHead.numberOfLocals(); ++i) {
         Node* node = root->variablesAtHead.local(i);

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGNode.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -74,7 +74,6 @@
     case Phi:
         return graph.m_form != SSA;
     case GetLocal:
-    case GetArgument:
     case SetLocal:
     case SetArgument:
     case Flush:

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2015-02-16 19:27:37 UTC (rev 180160)
@@ -61,7 +61,6 @@
     macro(KillLocal, NodeMustGenerate) \
     macro(MovHint, 0) \
     macro(ZombieHint, 0) \
-    macro(GetArgument, NodeResultJS | NodeMustGenerate) \
     macro(Phantom, NodeMustGenerate) \
     macro(HardPhantom, NodeMustGenerate) /* Like Phantom, but we never remove any of its children. */ \
     macro(Check, NodeMustGenerate) /* Used if we want just a type check but not liveness. Non-checking uses will be removed. */\

Modified: trunk/Source/_javascript_Core/dfg/DFGOSRAvailabilityAnalysisPhase.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGOSRAvailabilityAnalysisPhase.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGOSRAvailabilityAnalysisPhase.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -58,10 +58,11 @@
         
         BasicBlock* root = m_graph.block(0);
         root->ssa->availabilityAtHead.m_locals.fill(Availability::unavailable());
-        for (unsigned argument = root->ssa->availabilityAtHead.m_locals.numberOfArguments(); argument--;) {
-            root->ssa->availabilityAtHead.m_locals.argument(argument) =
-                Availability::unavailable().withFlush(
-                    FlushedAt(FlushedJSValue, virtualRegisterForArgument(argument)));
+        for (unsigned argument = m_graph.m_argumentFormats.size(); argument--;) {
+            FlushedAt flushedAt = FlushedAt(
+                m_graph.m_argumentFormats[argument],
+                virtualRegisterForArgument(argument));
+            root->ssa->availabilityAtHead.m_locals.argument(argument) = Availability(flushedAt);
         }
 
         // This could be made more efficient by processing blocks in reverse postorder.
@@ -138,7 +139,7 @@
         break;
     }
 
-    case GetArgument: {
+    case GetLocal: {
         VariableAccessData* variable = node->variableAccessData();
         m_availability.m_locals.operand(variable->local()) =
             Availability(node, variable->flushedAt());

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -552,7 +552,6 @@
             break;
             
         case Upsilon:
-        case GetArgument:
             // These don't get inserted until we go into SSA.
             RELEASE_ASSERT_NOT_REACHED();
             break;

Modified: trunk/Source/_javascript_Core/dfg/DFGPutLocalSinkingPhase.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGPutLocalSinkingPhase.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGPutLocalSinkingPhase.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -371,7 +371,7 @@
                     ssaCalculator.newDef(
                         operandToVariable.operand(node->local()), block, node->child1().node());
                     break;
-                case GetArgument:
+                case GetLocal:
                     ssaCalculator.newDef(
                         operandToVariable.operand(node->local()), block, node);
                     break;
@@ -450,13 +450,6 @@
                     break;
                 }
                     
-                case GetArgument: {
-                    VariableAccessData* variable = node->variableAccessData();
-                    VirtualRegister operand = variable->local();
-                    mapping.operand(operand) = node;
-                    break;
-                }
-                    
                 case KillLocal: {
                     deferred.operand(node->unlinkedLocal()) = VariableDeferral();
                     break;
@@ -489,6 +482,16 @@
                     preciseLocalClobberize(
                         m_graph, node, escapeHandler, escapeHandler,
                         [&] (VirtualRegister, Node*) { });
+                    
+                    // If we're a GetLocal, then we also create a mapping.
+                    // FIXME: We should be able to just eliminate such GetLocals, when we know
+                    // what their incoming value will be.
+                    // https://bugs.webkit.org/show_bug.cgi?id=141624
+                    if (node->op() == GetLocal) {
+                        VariableAccessData* variable = node->variableAccessData();
+                        VirtualRegister operand = variable->local();
+                        mapping.operand(operand) = node;
+                    }
                     break;
                 } }
             }

Modified: trunk/Source/_javascript_Core/dfg/DFGSSAConversionPhase.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGSSAConversionPhase.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGSSAConversionPhase.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -73,7 +73,7 @@
         }
         
         // Find all SetLocals and create Defs for them. We handle SetArgument by creating a
-        // GetArgument.
+        // GetLocal, and recording the flush format.
         for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
             BasicBlock* block = m_graph.block(blockIndex);
             if (!block)
@@ -97,7 +97,9 @@
                     ASSERT(node->op() == SetArgument);
                     childNode = m_insertionSet.insertNode(
                         nodeIndex, node->variableAccessData()->prediction(),
-                        GetArgument, node->origin, OpInfo(node->variableAccessData()));
+                        GetLocal, node->origin, OpInfo(node->variableAccessData()));
+                    m_argumentGetters.add(childNode);
+                    m_argumentMapping.add(node, childNode);
                 }
                 
                 m_calculator.newDef(
@@ -198,14 +200,14 @@
         //   - PhantomLocal becomes Phantom, and its child is whatever is specified by
         //     valueForOperand.
         //
-        //   - SetArgument is removed. Note that GetArgument nodes have already been inserted.
+        //   - SetArgument is removed. Note that GetLocal nodes have already been inserted.
         Operands<Node*> valueForOperand(OperandsLike, m_graph.block(0)->variablesAtHead);
         for (BasicBlock* block : m_graph.blocksInPreOrder()) {
             valueForOperand.clear();
             
             // CPS will claim that the root block has all arguments live. But we have already done
             // the first step of SSA conversion: argument locals are no longer live at head;
-            // instead we have GetArgument nodes for extracting the values of arguments. So, we
+            // instead we have GetLocal nodes for extracting the values of arguments. So, we
             // skip the at-head available value calculation for the root block.
             if (block != m_graph.block(0)) {
                 for (size_t i = valueForOperand.size(); i--;) {
@@ -293,9 +295,15 @@
                 }
                     
                 case GetLocal: {
+                    VariableAccessData* variable = node->variableAccessData();
+                    if (m_argumentGetters.contains(node)) {
+                        if (verbose)
+                            dataLog("Mapping: ", variable->local(), " -> ", node, "\n");
+                        valueForOperand.operand(variable->local()) = node;
+                        break;
+                    }
                     node->children.reset();
                     
-                    VariableAccessData* variable = node->variableAccessData();
                     if (variable->isCaptured())
                         break;
                     
@@ -337,15 +345,6 @@
                     break;
                 }
                     
-                case GetArgument: {
-                    VariableAccessData* variable = node->variableAccessData();
-                    ASSERT(!variable->isCaptured());
-                    if (verbose)
-                        dataLog("Mapping: ", variable->local(), " -> ", node, "\n");
-                    valueForOperand.operand(variable->local()) = node;
-                    break;
-                }
-                    
                 default:
                     break;
                 }
@@ -392,7 +391,21 @@
             block->ssa = std::make_unique<BasicBlock::SSAData>(block);
         }
         
-        m_graph.m_arguments.clear();
+        m_graph.m_argumentFormats.resize(m_graph.m_arguments.size());
+        for (unsigned i = m_graph.m_arguments.size(); i--;) {
+            FlushFormat format = FlushedJSValue;
+
+            Node* node = m_argumentMapping.get(m_graph.m_arguments[i]);
+
+            // m_argumentMapping.get could return null for a captured local. That's fine. We only
+            // track the argument loads of those arguments for which we speculate type. We don't
+            // speculate type for captured arguments.
+            if (node)
+                format = node->variableAccessData()->flushFormat();
+            
+            m_graph.m_argumentFormats[i] = format;
+            m_graph.m_arguments[i] = node; // Record the load that loads the arguments for the benefit of exit profiling.
+        }
         
         m_graph.m_form = SSA;
 
@@ -408,6 +421,8 @@
     SSACalculator m_calculator;
     InsertionSet m_insertionSet;
     HashMap<VariableAccessData*, SSACalculator::Variable*> m_ssaVariableForVariable;
+    HashMap<Node*, Node*> m_argumentMapping;
+    HashSet<Node*> m_argumentGetters;
     Vector<VariableAccessData*> m_variableForSSAIndex;
 };
 

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2015-02-16 19:27:37 UTC (rev 180160)
@@ -122,7 +122,6 @@
     case KillLocal:
     case MovHint:
     case ZombieHint:
-    case GetArgument:
     case Phantom:
     case HardPhantom:
     case Upsilon:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -4898,7 +4898,6 @@
     case LastNodeType:
     case Phi:
     case Upsilon:
-    case GetArgument:
     case ExtractOSREntryLocal:
     case CheckTierUpInLoop:
     case CheckTierUpAtReturn:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -4993,7 +4993,6 @@
     case LastNodeType:
     case Phi:
     case Upsilon:
-    case GetArgument:
     case ExtractOSREntryLocal:
     case CheckInBounds:
     case ArithIMul:

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -51,7 +51,6 @@
     case KillLocal:
     case MovHint:
     case ZombieHint:
-    case GetArgument:
     case Phantom:
     case HardPhantom:
     case Flush:

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp (180159 => 180160)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp	2015-02-16 19:05:06 UTC (rev 180159)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp	2015-02-16 19:27:37 UTC (rev 180160)
@@ -135,6 +135,8 @@
         m_prologue = FTL_NEW_BLOCK(m_out, ("Prologue"));
         LBasicBlock stackOverflow = FTL_NEW_BLOCK(m_out, ("Stack overflow"));
         m_handleExceptions = FTL_NEW_BLOCK(m_out, ("Handle Exceptions"));
+        
+        LBasicBlock checkArguments = FTL_NEW_BLOCK(m_out, ("Check arguments"));
 
         for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
             m_highBlock = m_graph.block(blockIndex);
@@ -193,7 +195,7 @@
         m_out.storePtr(m_out.constIntPtr(codeBlock()), addressFor(JSStack::CodeBlock));
         
         m_out.branch(
-            didOverflowStack(), rarely(stackOverflow), usually(lowBlock(m_graph.block(0))));
+            didOverflowStack(), rarely(stackOverflow), usually(checkArguments));
         
         m_out.appendTo(stackOverflow, m_handleExceptions);
         m_out.call(m_out.operation(operationThrowStackOverflowError), m_callFrame, m_out.constIntPtr(codeBlock()));
@@ -203,13 +205,58 @@
             m_out.constInt32(MacroAssembler::maxJumpReplacementSize()));
         m_out.unreachable();
         
-        m_out.appendTo(m_handleExceptions, lowBlock(m_graph.block(0)));
+        m_out.appendTo(m_handleExceptions, checkArguments);
         m_ftlState.handleExceptionStackmapID = m_stackmapIDs++;
         m_out.call(
             m_out.stackmapIntrinsic(), m_out.constInt64(m_ftlState.handleExceptionStackmapID),
             m_out.constInt32(MacroAssembler::maxJumpReplacementSize()));
         m_out.unreachable();
-
+        
+        m_out.appendTo(checkArguments, lowBlock(m_graph.block(0)));
+        availabilityMap().clear();
+        availabilityMap().m_locals = Operands<Availability>(codeBlock()->numParameters(), 0);
+        for (unsigned i = codeBlock()->numParameters(); i--;) {
+            availabilityMap().m_locals.argument(i) =
+                Availability(FlushedAt(FlushedJSValue, virtualRegisterForArgument(i)));
+        }
+        m_codeOriginForExitTarget = CodeOrigin(0);
+        m_codeOriginForExitProfile = CodeOrigin(0);
+        m_node = nullptr;
+        for (unsigned i = codeBlock()->numParameters(); i--;) {
+            Node* node = m_graph.m_arguments[i];
+            VirtualRegister operand = virtualRegisterForArgument(i);
+            
+            LValue jsValue = m_out.load64(addressFor(operand));
+            
+            if (node) {
+                DFG_ASSERT(m_graph, node, operand == node->variableAccessData()->machineLocal());
+                
+                // This is a hack, but it's an effective one. It allows us to do CSE on the
+                // primordial load of arguments. This assumes that the GetLocal that got put in
+                // place of the original SetArgument doesn't have any effects before it. This
+                // should hold true.
+                m_loadedArgumentValues.add(node, jsValue);
+            }
+            
+            switch (m_graph.m_argumentFormats[i]) {
+            case FlushedInt32:
+                speculate(BadType, jsValueValue(jsValue), node, isNotInt32(jsValue));
+                break;
+            case FlushedBoolean:
+                speculate(BadType, jsValueValue(jsValue), node, isNotBoolean(jsValue));
+                break;
+            case FlushedCell:
+                speculate(BadType, jsValueValue(jsValue), node, isNotCell(jsValue));
+                break;
+            case FlushedJSValue:
+                break;
+            default:
+                DFG_CRASH(m_graph, node, "Bad flush format for argument");
+                break;
+            }
+        }
+        m_out.jump(lowBlock(m_graph.block(0)));
+        
         for (BasicBlock* block : preOrder)
             compileBlock(block);
         
@@ -381,9 +428,6 @@
         case BooleanToNumber:
             compileBooleanToNumber();
             break;
-        case GetArgument:
-            compileGetArgument();
-            break;
         case ExtractOSREntryLocal:
             compileExtractOSREntryLocal();
             break;
@@ -981,36 +1025,6 @@
         }
     }
 
-    void compileGetArgument()
-    {
-        VariableAccessData* variable = m_node->variableAccessData();
-        VirtualRegister operand = variable->machineLocal();
-        DFG_ASSERT(m_graph, m_node, operand.isArgument());
-
-        LValue jsValue = m_out.load64(addressFor(operand));
-
-        switch (useKindFor(variable->flushFormat())) {
-        case Int32Use:
-            speculate(BadType, jsValueValue(jsValue), m_node, isNotInt32(jsValue));
-            setInt32(unboxInt32(jsValue));
-            break;
-        case CellUse:
-            speculate(BadType, jsValueValue(jsValue), m_node, isNotCell(jsValue));
-            setJSValue(jsValue);
-            break;
-        case BooleanUse:
-            speculate(BadType, jsValueValue(jsValue), m_node, isNotBoolean(jsValue));
-            setBoolean(unboxBoolean(jsValue));
-            break;
-        case UntypedUse:
-            setJSValue(jsValue);
-            break;
-        default:
-            DFG_CRASH(m_graph, m_node, "Bad use kind");
-            break;
-        }
-    }
-    
     void compileExtractOSREntryLocal()
     {
         EncodedJSValue* buffer = static_cast<EncodedJSValue*>(
@@ -1020,13 +1034,16 @@
     
     void compileGetLocal()
     {
-        // GetLocals arise only for captured variables.
+        // GetLocals arise only for captured variables and arguments. For arguments, we might have
+        // already loaded it.
+        if (LValue value = m_loadedArgumentValues.get(m_node)) {
+            setJSValue(value);
+            return;
+        }
         
         VariableAccessData* variable = m_node->variableAccessData();
         AbstractValue& value = m_state.variables().operand(variable->local());
         
-        DFG_ASSERT(m_graph, m_node, variable->isCaptured());
-        
         if (isInt32Speculation(value.m_type))
             setInt32(m_out.load32(payloadFor(variable->machineLocal())));
         else
@@ -7014,6 +7031,11 @@
     HashMap<Node*, LoweredNodeValue> m_storageValues;
     HashMap<Node*, LoweredNodeValue> m_doubleValues;
     
+    // This is a bit of a hack. It prevents LLVM from having to do CSE on loading of arguments.
+    // It's nice to have these optimizations on our end because we can guarantee them a bit better.
+    // Probably also saves LLVM compile time.
+    HashMap<Node*, LValue> m_loadedArgumentValues;
+    
     HashMap<Node*, LValue> m_phis;
     
     LocalOSRAvailabilityCalculator m_availabilityCalculator;

Added: trunk/Source/_javascript_Core/tests/stress/dead-speculating-argument-use.js (0 => 180160)


--- trunk/Source/_javascript_Core/tests/stress/dead-speculating-argument-use.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/dead-speculating-argument-use.js	2015-02-16 19:27:37 UTC (rev 180160)
@@ -0,0 +1,17 @@
+function foo(x) {
+    var tmp = x + 1;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 10000; ++i)
+    foo(i);
+
+var didCall = false;
+var o = {
+    valueOf: function() { didCall = true; }
+};
+
+foo(o);
+if (!didCall)
+    throw "Error: didn't call valueOf";
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to