Title: [283623] trunk
Revision
283623
Author
sbar...@apple.com
Date
2021-10-06 08:49:42 -0700 (Wed, 06 Oct 2021)

Log Message

Run backwards propagation before we prune the graph after ForceOSRExit nodes in BytecodeParser
https://bugs.webkit.org/show_bug.cgi?id=230823
<rdar://problem/83565088>

Reviewed by Robin Morisset.

JSTests:

* run-backwards-propagation-before-osr-exit-pruning.js: Added.
(assert):
(main.async v24):
(main):

Source/_javascript_Core:

We've found yet another bug where pruning code after OSR exits
before running backwards propagation leads to us breaking the spec
in weird IR situations. In the particular test case here, we end
up not thinking we care about negative zero for an ArithNegate,
and we exit the program while recovering the value 0 instead of -0.

Fundamentally, backwards propagation wants to see all bytecode uses.
Therefore, it seems like a more sound strategy to run backwards propagation
before we end up mucking with the graph. This patch makes it so we run
backwards propagation inside bytecode parser before we prune the IR.
That way, the phase sees the graph as if it's an IR over the whole bytecode
graph.

* bytecode/Operands.h:
(JSC::Operands::operator!= const):
* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::BackwardsPropagationPhase):
(JSC::DFG::BackwardsPropagationPhase::run):
(JSC::DFG::BackwardsPropagationPhase::mergeFlags):
(JSC::DFG::BackwardsPropagationPhase::propagate):
(JSC::DFG::performBackwardsPropagation):
* dfg/DFGBackwardsPropagationPhase.h:
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parse):
* dfg/DFGPlan.cpp:
(JSC::DFG::Plan::compileInThreadImpl):
* dfg/DFGUnificationPhase.cpp:
(JSC::DFG::UnificationPhase::run):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (283622 => 283623)


--- trunk/JSTests/ChangeLog	2021-10-06 15:43:37 UTC (rev 283622)
+++ trunk/JSTests/ChangeLog	2021-10-06 15:49:42 UTC (rev 283623)
@@ -1,3 +1,16 @@
+2021-10-06  Saam Barati  <sbar...@apple.com>
+
+        Run backwards propagation before we prune the graph after ForceOSRExit nodes in BytecodeParser
+        https://bugs.webkit.org/show_bug.cgi?id=230823
+        <rdar://problem/83565088>
+
+        Reviewed by Robin Morisset.
+
+        * run-backwards-propagation-before-osr-exit-pruning.js: Added.
+        (assert):
+        (main.async v24):
+        (main):
+
 2021-10-05  Saam Barati  <sbar...@apple.com>
 
         Don't pass DontBuildStrings to next token after parsing an empty parameter list

Added: trunk/JSTests/run-backwards-propagation-before-osr-exit-pruning.js (0 => 283623)


--- trunk/JSTests/run-backwards-propagation-before-osr-exit-pruning.js	                        (rev 0)
+++ trunk/JSTests/run-backwards-propagation-before-osr-exit-pruning.js	2021-10-06 15:49:42 UTC (rev 283623)
@@ -0,0 +1,24 @@
+//@ runDefault("--validateOptions=true", "--useConcurrentJIT=false", "--useConcurrentGC=false", "--thresholdForJITSoon=10", "--thresholdForJITAfterWarmUp=10", "--thresholdForOptimizeAfterWarmUp=100", "--thresholdForOptimizeAfterLongWarmUp=100", "--thresholdForOptimizeSoon=100", "--thresholdForFTLOptimizeAfterWarmUp=1000", "--thresholdForFTLOptimizeSoon=1000", "--validateBCE=true", "--useFTLJIT=true")
+
+function assert(b) {
+    if (!b)
+        throw new Error;
+}
+function main() {
+    let v38;
+    let v40;
+
+    async function v24() {
+        const v33 = false;
+        const v34 = -v33;
+        const v37 = typeof search;
+        const v39 = v38 ? v30 : 1;
+        v40 = v34;
+            
+        for (let v41 = 0; v41 != 100000; v41++) { }
+    }
+    [1,1,1].filter(v24);
+    assert(Object.is(v40, -0) === true);
+    assert(Object.is(v40, 0) === false);
+}
+main();

Modified: trunk/Source/_javascript_Core/ChangeLog (283622 => 283623)


--- trunk/Source/_javascript_Core/ChangeLog	2021-10-06 15:43:37 UTC (rev 283622)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-10-06 15:49:42 UTC (rev 283623)
@@ -1,3 +1,40 @@
+2021-10-06  Saam Barati  <sbar...@apple.com>
+
+        Run backwards propagation before we prune the graph after ForceOSRExit nodes in BytecodeParser
+        https://bugs.webkit.org/show_bug.cgi?id=230823
+        <rdar://problem/83565088>
+
+        Reviewed by Robin Morisset.
+
+        We've found yet another bug where pruning code after OSR exits
+        before running backwards propagation leads to us breaking the spec
+        in weird IR situations. In the particular test case here, we end
+        up not thinking we care about negative zero for an ArithNegate,
+        and we exit the program while recovering the value 0 instead of -0.
+        
+        Fundamentally, backwards propagation wants to see all bytecode uses.
+        Therefore, it seems like a more sound strategy to run backwards propagation
+        before we end up mucking with the graph. This patch makes it so we run
+        backwards propagation inside bytecode parser before we prune the IR.
+        That way, the phase sees the graph as if it's an IR over the whole bytecode
+        graph.
+
+        * bytecode/Operands.h:
+        (JSC::Operands::operator!= const):
+        * dfg/DFGBackwardsPropagationPhase.cpp:
+        (JSC::DFG::BackwardsPropagationPhase::BackwardsPropagationPhase):
+        (JSC::DFG::BackwardsPropagationPhase::run):
+        (JSC::DFG::BackwardsPropagationPhase::mergeFlags):
+        (JSC::DFG::BackwardsPropagationPhase::propagate):
+        (JSC::DFG::performBackwardsPropagation):
+        * dfg/DFGBackwardsPropagationPhase.h:
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parse):
+        * dfg/DFGPlan.cpp:
+        (JSC::DFG::Plan::compileInThreadImpl):
+        * dfg/DFGUnificationPhase.cpp:
+        (JSC::DFG::UnificationPhase::run):
+
 2021-10-06  Xan López  <x...@igalia.com>
 
         [JSC][32bit] Remove a bunch of compiler warnings

Modified: trunk/Source/_javascript_Core/bytecode/Operands.h (283622 => 283623)


--- trunk/Source/_javascript_Core/bytecode/Operands.h	2021-10-06 15:43:37 UTC (rev 283622)
+++ trunk/Source/_javascript_Core/bytecode/Operands.h	2021-10-06 15:49:42 UTC (rev 283623)
@@ -400,6 +400,11 @@
         
         return m_values == other.m_values;
     }
+
+    bool operator!=(const Operands& other) const
+    {
+        return !(*this == other);
+    }
     
     void dumpInContext(PrintStream& out, DumpContext* context) const;
     void dump(PrintStream& out) const;

Modified: trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp (283622 => 283623)


--- trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp	2021-10-06 15:43:37 UTC (rev 283622)
+++ trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp	2021-10-06 15:49:42 UTC (rev 283623)
@@ -34,22 +34,48 @@
 
 namespace JSC { namespace DFG {
 
-class BackwardsPropagationPhase : public Phase {
+// This phase is run at the end of BytecodeParsing, so the graph isn't in a fully formed state.
+// For example, we can't access the predecessor list of any basic blocks yet.
+
+class BackwardsPropagationPhase {
 public:
     BackwardsPropagationPhase(Graph& graph)
-        : Phase(graph, "backwards propagation")
+        : m_graph(graph)
+        , m_flagsAtHead(graph)
     {
     }
     
     bool run()
     {
-        m_changed = true;
-        while (m_changed) {
-            m_changed = false;
+        for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
+            m_flagsAtHead[block] = Operands<NodeFlags>(OperandsLike, m_graph.block(0)->variablesAtHead);
+            m_flagsAtHead[block].fill(0);
+        }
+
+        bool changed;
+        do {
+            changed = false;
+
             for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
                 BasicBlock* block = m_graph.block(blockIndex);
                 if (!block)
                     continue;
+
+                {
+                    unsigned numSuccessors = block->numSuccessors();
+                    if (!numSuccessors) {
+                        m_currentFlags = Operands<NodeFlags>(OperandsLike, m_graph.block(0)->variablesAtHead);
+                        m_currentFlags.fill(0);
+                    } else {
+                        m_currentFlags = m_flagsAtHead[block->successor(0)];
+                        for (unsigned i = 1; i < numSuccessors; ++i) {
+                            BasicBlock* successor = block->successor(i);
+                            for (size_t i = 0; i < m_currentFlags.size(); ++i)
+                                m_currentFlags[i] |= m_flagsAtHead[successor][i];
+                        }
+                    }
+                }
+
             
                 // Prevent a tower of overflowing additions from creating a value that is out of the
                 // safe 2^48 range.
@@ -57,8 +83,13 @@
             
                 for (unsigned indexInBlock = block->size(); indexInBlock--;)
                     propagate(block->at(indexInBlock));
+
+                if (m_flagsAtHead[block] != m_currentFlags) {
+                    m_flagsAtHead[block] = m_currentFlags;
+                    changed = true;
+                }
             }
-        }
+        } while (changed);
         
         return true;
     }
@@ -153,6 +184,11 @@
         return isWithinPowerOfTwo<power>(edge.node());
     }
 
+    static bool mergeFlags(NodeFlags& flagsRef, NodeFlags newFlags)
+    {
+        return checkAndSet(flagsRef, flagsRef | newFlags);
+    }
+
     bool mergeDefaultFlags(Node* node)
     {
         bool changed = false;
@@ -184,25 +220,36 @@
         switch (node->op()) {
         case GetLocal: {
             VariableAccessData* variableAccessData = node->variableAccessData();
-            flags &= ~NodeBytecodeUsesAsInt; // We don't care about cross-block uses-as-int.
-            m_changed |= variableAccessData->mergeFlags(flags);
+            NodeFlags& flagsRef = m_currentFlags.operand(variableAccessData->operand());
+            mergeFlags(flagsRef, flags);
+            variableAccessData->mergeFlags(flagsRef & ~NodeBytecodeUsesAsInt); // We don't care about cross-block uses-as-int for this.
             break;
         }
             
         case SetLocal: {
             VariableAccessData* variableAccessData = node->variableAccessData();
-            if (!variableAccessData->isLoadedFrom())
+
+            Operand operand = variableAccessData->operand();
+            NodeFlags flags = m_currentFlags.operand(operand);
+            if (!flags)
                 break;
-            flags = variableAccessData->flags();
+
             RELEASE_ASSERT(!(flags & ~NodeBytecodeBackPropMask));
-            flags |= NodeBytecodeUsesAsNumber; // Account for the fact that control flow may cause overflows that our modeling can't handle.
-            node->child1()->mergeFlags(flags);
+
+            variableAccessData->mergeFlags(flags);
+            // We union with NodeBytecodeUsesAsNumber to account for the fact that control flow may cause overflows that our modeling can't handle.
+            // For example, a loop where we always add a constant value.
+            node->child1()->mergeFlags(flags | NodeBytecodeUsesAsNumber); 
+
+            m_currentFlags.operand(operand) = 0;
             break;
         }
             
         case Flush: {
             VariableAccessData* variableAccessData = node->variableAccessData();
-            m_changed |= variableAccessData->mergeFlags(NodeBytecodeUsesAsValue);
+            NodeFlags& flagsRef = m_currentFlags.operand(variableAccessData->operand());
+            mergeFlags(flagsRef, NodeBytecodeUsesAsValue);
+            variableAccessData->mergeFlags(flagsRef);
             break;
         }
             
@@ -491,13 +538,16 @@
         }
     }
     
+    Graph& m_graph;
     bool m_allowNestedOverflowingAdditions;
-    bool m_changed;
+
+    BlockMap<Operands<NodeFlags>> m_flagsAtHead;
+    Operands<NodeFlags> m_currentFlags;
 };
 
-bool performBackwardsPropagation(Graph& graph)
+void performBackwardsPropagation(Graph& graph)
 {
-    return runPhase<BackwardsPropagationPhase>(graph);
+    BackwardsPropagationPhase(graph).run();
 }
 
 } } // namespace JSC::DFG

Modified: trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.h (283622 => 283623)


--- trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.h	2021-10-06 15:43:37 UTC (rev 283622)
+++ trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.h	2021-10-06 15:49:42 UTC (rev 283623)
@@ -35,7 +35,7 @@
 // Infer basic information about how nodes are used by doing a block-local
 // backwards flow analysis.
 
-bool performBackwardsPropagation(Graph&);
+void performBackwardsPropagation(Graph&);
 
 } } // namespace JSC::DFG
 

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (283622 => 283623)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2021-10-06 15:43:37 UTC (rev 283622)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2021-10-06 15:49:42 UTC (rev 283623)
@@ -43,6 +43,7 @@
 #include "CommonSlowPaths.h"
 #include "DFGAbstractHeap.h"
 #include "DFGArrayMode.h"
+#include "DFGBackwardsPropagationPhase.h"
 #include "DFGBlockSet.h"
 #include "DFGCapabilities.h"
 #include "DFGClobberize.h"
@@ -9018,6 +9019,13 @@
     parseCodeBlock();
     linkBlocks(inlineStackEntry.m_unlinkedBlocks, inlineStackEntry.m_blockLinkingTargets);
 
+    // We run backwards propagation now because the soundness of that phase
+    // relies on seeing the graph as if it were an IR over bytecode, since
+    // the spec-correctness of that phase relies on seeing all bytecode uses.
+    // Therefore, we run this pass before we do any pruning of the graph
+    // after ForceOSRExit sites.
+    performBackwardsPropagation(m_graph);
+
     if (m_hasAnyForceOSRExits) {
         BlockSet blocksToIgnore;
         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
@@ -9076,15 +9084,17 @@
 
                 ++nodeIndex;
 
+                if (validationEnabled()) {
+                    // This verifies that we don't need to change any of the successors's predecessor
+                    // list after planting the Unreachable below. At this point in the bytecode
+                    // parser, we haven't linked up the predecessor lists yet.
+                    for (BasicBlock* successor : block->successors())
+                        RELEASE_ASSERT(successor->predecessors.isEmpty());
+                }
+
+                block->resize(nodeIndex);
+
                 {
-                    if (validationEnabled()) {
-                        // This verifies that we don't need to change any of the successors's predecessor
-                        // list after planting the Unreachable below. At this point in the bytecode
-                        // parser, we haven't linked up the predecessor lists yet.
-                        for (BasicBlock* successor : block->successors())
-                            RELEASE_ASSERT(successor->predecessors.isEmpty());
-                    }
-
                     auto insertLivenessPreservingOp = [&] (InlineCallFrame* inlineCallFrame, NodeType op, Operand operand) {
                         VariableAccessData* variable = mapping.operand(operand);
                         if (!variable) {
@@ -9108,41 +9118,9 @@
                     flushForTerminalImpl(origin.semantic, addFlushDirect, addPhantomLocalDirect);
                 }
 
-                while (true) {
-                    RELEASE_ASSERT(nodeIndex < block->size());
-
-                    Node* node = block->at(nodeIndex);
-
-                    node->origin = origin;
-                    m_graph.doToChildren(node, [&] (Edge edge) {
-                        // We only need to keep data flow edges to nodes defined prior to the ForceOSRExit. The reason
-                        // for this is we rely on backwards propagation being able to see the "full" bytecode. To model
-                        // this, we preserve uses of a node in a generic way so that backwards propagation can reason
-                        // about them. Therefore, we can't remove uses of a node which is defined before the ForceOSRExit
-                        // even when we're at a point in the program after the ForceOSRExit, because that would break backwards
-                        // propagation's analysis over the uses of a node. However, we don't need this same preservation for
-                        // nodes defined after ForceOSRExit, as we've already exitted before those defs.
-                        if (edge->hasResult())
-                            insertionSet.insertNode(nodeIndex, SpecNone, Phantom, origin, Edge(edge.node(), UntypedUse));
-                    });
-
-                    bool isTerminal = node->isTerminal();
-
-                    node->removeWithoutChecks();
-
-                    if (isTerminal) {
-                        insertionSet.insertNode(nodeIndex, SpecNone, Unreachable, origin);
-                        break;
-                    }
-
-                    ++nodeIndex;
-                }
-
+                insertionSet.insertNode(nodeIndex, SpecNone, Unreachable, origin);
                 insertionSet.execute(block);
 
-                auto nodeAndIndex = block->findTerminal();
-                DFG_ASSERT(m_graph, nodeAndIndex.node, nodeAndIndex.node->op() == Unreachable);
-                block->resize(nodeAndIndex.index + 1);
                 break;
             }
         }

Modified: trunk/Source/_javascript_Core/dfg/DFGPlan.cpp (283622 => 283623)


--- trunk/Source/_javascript_Core/dfg/DFGPlan.cpp	2021-10-06 15:43:37 UTC (rev 283622)
+++ trunk/Source/_javascript_Core/dfg/DFGPlan.cpp	2021-10-06 15:49:42 UTC (rev 283623)
@@ -29,7 +29,6 @@
 #if ENABLE(DFG_JIT)
 
 #include "DFGArgumentsEliminationPhase.h"
-#include "DFGBackwardsPropagationPhase.h"
 #include "DFGByteCodeParser.h"
 #include "DFGCFAPhase.h"
 #include "DFGCFGSimplificationPhase.h"
@@ -255,7 +254,6 @@
     if (validationEnabled())
         validate(dfg);
     
-    RUN_PHASE(performBackwardsPropagation);
     RUN_PHASE(performPredictionPropagation);
     RUN_PHASE(performFixup);
     RUN_PHASE(performInvalidationPointInjection);

Modified: trunk/Source/_javascript_Core/dfg/DFGUnificationPhase.cpp (283622 => 283623)


--- trunk/Source/_javascript_Core/dfg/DFGUnificationPhase.cpp	2021-10-06 15:43:37 UTC (rev 283622)
+++ trunk/Source/_javascript_Core/dfg/DFGUnificationPhase.cpp	2021-10-06 15:49:42 UTC (rev 283623)
@@ -75,6 +75,7 @@
             data->find()->mergeShouldNeverUnbox(data->shouldNeverUnbox());
             data->find()->mergeIsLoadedFrom(data->isLoadedFrom());
             data->find()->mergeIsProfitableToUnbox(data->isProfitableToUnbox());
+            data->find()->mergeFlags(data->flags());
         }
         
         m_graph.m_unificationState = GloballyUnified;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to