Title: [171400] branches/safari-600.1-branch/Source/_javascript_Core

Diff

Modified: branches/safari-600.1-branch/Source/_javascript_Core/ChangeLog (171399 => 171400)


--- branches/safari-600.1-branch/Source/_javascript_Core/ChangeLog	2014-07-23 05:09:52 UTC (rev 171399)
+++ branches/safari-600.1-branch/Source/_javascript_Core/ChangeLog	2014-07-23 05:11:48 UTC (rev 171400)
@@ -1,3 +1,48 @@
+2014-07-22 Dana Burkart <dburk...@apple.com>
+    
+        Merge r171190.
+
+    2014-07-16  Filip Pizlo  <fpi...@apple.com>
+
+            DFG Flush(SetLocal) store elimination is overzealous for captured variables in the presence of nodes that have no effects but may throw
+            https://bugs.webkit.org/show_bug.cgi?id=134988
+            <rdar://problem/17706349>
+
+            Reviewed by Oliver Hunt.
+            
+            Luckily, we also don't need this optimization to be super powerful: the only place
+            where it really matters is for getting rid of the redundancy between op_enter and
+            op_init_lazy_reg, and in that case, there is a small set of possible nodes between the
+            two things. This change updates the store eliminator to know about only that small,
+            obviously safe, set of nodes over which we can store-eliminate.
+            
+            This shouldn't have any performance impact in the DFG because this optimization kicks
+            in relatively rarely already. And once we tier up into the FTL, we get a much better
+            store elimination over LLVM IR, so this really shouldn't matter at all.
+            
+            The tricky part of this patch is that there is a close relative of this optimization,
+            for uncaptured variables that got flushed. This happens for arguments to inlined calls.
+            I make this work by splitting it into two different store eliminators.
+            
+            Note that in the process of crafting the tests, I realized that we were incorrectly
+            DCEing NewArrayWithSize. That's not cool, since that can throw an exception for
+            negative array sizes. If we ever did want to DCE this node, we'd need to lower the node
+            to a check node followed by the actual allocation.
+
+            * dfg/DFGCSEPhase.cpp:
+            (JSC::DFG::CSEPhase::uncapturedSetLocalStoreElimination):
+            (JSC::DFG::CSEPhase::capturedSetLocalStoreElimination):
+            (JSC::DFG::CSEPhase::setLocalStoreElimination):
+            (JSC::DFG::CSEPhase::performNodeCSE):
+            (JSC::DFG::CSEPhase::SetLocalStoreEliminationResult::SetLocalStoreEliminationResult): Deleted.
+            * dfg/DFGNodeType.h:
+            * tests/stress/capture-escape-and-throw.js: Added.
+            (foo.f):
+            (foo):
+            * tests/stress/new-array-with-size-throw-exception-and-tear-off-arguments.js: Added.
+            (foo):
+            (bar):
+
 2014-07-17  Dean Jackson  <d...@apple.com>
 
         <rdar://problem/17675068> Disable some features on this branch.

Modified: branches/safari-600.1-branch/Source/_javascript_Core/dfg/DFGCSEPhase.cpp (171399 => 171400)


--- branches/safari-600.1-branch/Source/_javascript_Core/dfg/DFGCSEPhase.cpp	2014-07-23 05:09:52 UTC (rev 171399)
+++ branches/safari-600.1-branch/Source/_javascript_Core/dfg/DFGCSEPhase.cpp	2014-07-23 05:11:48 UTC (rev 171400)
@@ -928,48 +928,34 @@
         return 0;
     }
     
-    struct SetLocalStoreEliminationResult {
-        SetLocalStoreEliminationResult()
-            : mayBeAccessed(false)
-            , mayExit(false)
-            , mayClobberWorld(false)
-        {
-        }
-        
-        bool mayBeAccessed;
-        bool mayExit;
-        bool mayClobberWorld;
-    };
-    SetLocalStoreEliminationResult setLocalStoreElimination(
-        VirtualRegister local, Node* expectedNode)
+    bool uncapturedSetLocalStoreElimination(VirtualRegister local, Node* expectedNode)
     {
-        SetLocalStoreEliminationResult result;
         for (unsigned i = m_indexInBlock; i--;) {
             Node* node = m_currentBlock->at(i);
             switch (node->op()) {
             case GetLocal:
             case Flush:
                 if (node->local() == local)
-                    result.mayBeAccessed = true;
+                    return false;
                 break;
                 
             case GetLocalUnlinked:
                 if (node->unlinkedLocal() == local)
-                    result.mayBeAccessed = true;
+                    return false;
                 break;
                 
             case SetLocal: {
                 if (node->local() != local)
                     break;
                 if (node != expectedNode)
-                    result.mayBeAccessed = true;
-                return result;
+                    return false;
+                return true;
             }
                 
             case GetClosureVar:
             case PutClosureVar:
                 if (static_cast<VirtualRegister>(node->varNumber()) == local)
-                    result.mayBeAccessed = true;
+                    return false;
                 break;
                 
             case GetMyScope:
@@ -977,25 +963,24 @@
                 if (node->origin.semantic.inlineCallFrame)
                     break;
                 if (m_graph.uncheckedActivationRegister() == local)
-                    result.mayBeAccessed = true;
+                    return false;
                 break;
                 
             case CheckArgumentsNotCreated:
             case GetMyArgumentsLength:
             case GetMyArgumentsLengthSafe:
                 if (m_graph.uncheckedArgumentsRegisterFor(node->origin.semantic) == local)
-                    result.mayBeAccessed = true;
+                    return false;
                 break;
                 
             case GetMyArgumentByVal:
             case GetMyArgumentByValSafe:
-                result.mayBeAccessed = true;
-                break;
+                return false;
                 
             case GetByVal:
                 // If this is accessing arguments then it's potentially accessing locals.
                 if (node->arrayMode().type() == Array::Arguments)
-                    result.mayBeAccessed = true;
+                    return false;
                 break;
                 
             case CreateArguments:
@@ -1005,21 +990,66 @@
                 // are live. We could be clever here and check if the local qualifies as an
                 // argument register. But that seems like it would buy us very little since
                 // any kind of tear offs are rare to begin with.
-                result.mayBeAccessed = true;
-                break;
+                return false;
                 
             default:
                 break;
             }
-            result.mayExit |= node->canExit();
-            result.mayClobberWorld |= m_graph.clobbersWorld(node);
+            if (m_graph.clobbersWorld(node))
+                return false;
         }
         RELEASE_ASSERT_NOT_REACHED();
-        // Be safe in release mode.
-        result.mayBeAccessed = true;
-        return result;
+        return false;
     }
+
+    bool capturedSetLocalStoreElimination(VirtualRegister local, Node* expectedNode)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            switch (node->op()) {
+            case GetLocal:
+            case Flush:
+                if (node->local() == local)
+                    return false;
+                break;
+                
+            case GetLocalUnlinked:
+                if (node->unlinkedLocal() == local)
+                    return false;
+                break;
+                
+            case SetLocal: {
+                if (node->local() != local)
+                    break;
+                if (node != expectedNode)
+                    return false;
+                return true;
+            }
+                
+            case Phantom:
+            case Check:
+            case HardPhantom:
+            case MovHint:
+            case JSConstant:
+            case DoubleConstant:
+            case Int52Constant:
+                break;
+                
+            default:
+                return false;
+            }
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+        return false;
+    }
     
+    bool setLocalStoreElimination(VariableAccessData* variableAccessData, Node* expectedNode)
+    {
+        if (variableAccessData->isCaptured())
+            return capturedSetLocalStoreElimination(variableAccessData->local(), expectedNode);
+        return uncapturedSetLocalStoreElimination(variableAccessData->local(), expectedNode);
+    }
+    
     bool invalidationPointElimination()
     {
         for (unsigned i = m_indexInBlock; i--;) {
@@ -1215,7 +1245,6 @@
         case Flush: {
             ASSERT(m_graph.m_form != SSA);
             VariableAccessData* variableAccessData = node->variableAccessData();
-            VirtualRegister local = variableAccessData->local();
             if (!node->child1()) {
                 // FIXME: It's silly that we punt on flush-eliminating here. We don't really
                 // need child1 to figure out what's going on.
@@ -1239,12 +1268,9 @@
                 if (replacement->canExit())
                     break;
             }
-            SetLocalStoreEliminationResult result =
-                setLocalStoreElimination(local, replacement);
-            if (result.mayBeAccessed || result.mayClobberWorld)
+            if (!setLocalStoreElimination(variableAccessData, replacement))
                 break;
             ASSERT(replacement->op() == SetLocal);
-            // FIXME: Investigate using mayExit as a further optimization.
             node->convertToPhantom();
             Node* dataNode = replacement->child1().node();
             ASSERT(dataNode->hasResult());

Modified: branches/safari-600.1-branch/Source/_javascript_Core/dfg/DFGNodeType.h (171399 => 171400)


--- branches/safari-600.1-branch/Source/_javascript_Core/dfg/DFGNodeType.h	2014-07-23 05:09:52 UTC (rev 171399)
+++ branches/safari-600.1-branch/Source/_javascript_Core/dfg/DFGNodeType.h	2014-07-23 05:11:48 UTC (rev 171400)
@@ -233,7 +233,7 @@
     /* Allocations. */\
     macro(NewObject, NodeResultJS) \
     macro(NewArray, NodeResultJS | NodeHasVarArgs) \
-    macro(NewArrayWithSize, NodeResultJS) \
+    macro(NewArrayWithSize, NodeResultJS | NodeMustGenerate) \
     macro(NewArrayBuffer, NodeResultJS) \
     macro(NewTypedArray, NodeResultJS | NodeClobbersWorld | NodeMustGenerate) \
     macro(NewRegexp, NodeResultJS) \

Copied: branches/safari-600.1-branch/Source/_javascript_Core/tests/stress/capture-escape-and-throw.js (from rev 171190, trunk/Source/_javascript_Core/tests/stress/capture-escape-and-throw.js) (0 => 171400)


--- branches/safari-600.1-branch/Source/_javascript_Core/tests/stress/capture-escape-and-throw.js	                        (rev 0)
+++ branches/safari-600.1-branch/Source/_javascript_Core/tests/stress/capture-escape-and-throw.js	2014-07-23 05:11:48 UTC (rev 171400)
@@ -0,0 +1,28 @@
+var f;
+
+function foo(s) {
+    var x = 1;
+    f = function() { return x; };
+    x = 2;
+    new Array(s);
+    x = 3;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 10000; ++i)
+    foo(1);
+
+var didThrow = false;
+try {
+    foo(-1);
+} catch (e) {
+    didThrow = e;
+}
+
+if (("" + didThrow).indexOf("RangeError") != 0)
+    throw "Error: did not throw right exception: " + didThrow;
+
+var result = f();
+if (result != 2)
+    throw "Error: bad result from f(): " + result;

Copied: branches/safari-600.1-branch/Source/_javascript_Core/tests/stress/new-array-with-size-throw-exception-and-tear-off-arguments.js (from rev 171190, trunk/Source/_javascript_Core/tests/stress/new-array-with-size-throw-exception-and-tear-off-arguments.js) (0 => 171400)


--- branches/safari-600.1-branch/Source/_javascript_Core/tests/stress/new-array-with-size-throw-exception-and-tear-off-arguments.js	                        (rev 0)
+++ branches/safari-600.1-branch/Source/_javascript_Core/tests/stress/new-array-with-size-throw-exception-and-tear-off-arguments.js	2014-07-23 05:11:48 UTC (rev 171400)
@@ -0,0 +1,26 @@
+function foo() {
+    var a = arguments;
+    return new Array(a[0]);
+}
+
+function bar(x) {
+    return foo(x);
+}
+
+noInline(bar);
+
+for (var i = 0; i < 10000; ++i) {
+    var result = bar(42);
+    if (result.length != 42)
+        throw "Error: bad result length: " + result;
+}
+
+var didThrow = false;
+try {
+    bar(-1);
+} catch (e) {
+    didThrow = e;
+}
+
+if (("" + didThrow).indexOf("RangeError") != 0)
+    throw "Error: did not throw right exception: " + didThrow;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to