]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - dfg/DFGPredictionPropagationPhase.cpp
JavaScriptCore-1097.3.tar.gz
[apple/javascriptcore.git] / dfg / DFGPredictionPropagationPhase.cpp
diff --git a/dfg/DFGPredictionPropagationPhase.cpp b/dfg/DFGPredictionPropagationPhase.cpp
new file mode 100644 (file)
index 0000000..5317460
--- /dev/null
@@ -0,0 +1,843 @@
+/*
+ * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "DFGPredictionPropagationPhase.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGGraph.h"
+#include "DFGPhase.h"
+
+namespace JSC { namespace DFG {
+
+class PredictionPropagationPhase : public Phase {
+public:
+    PredictionPropagationPhase(Graph& graph)
+        : Phase(graph, "prediction propagation")
+    {
+    }
+    
+    void run()
+    {
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+        m_count = 0;
+#endif
+        // 1) propagate predictions
+
+        do {
+            m_changed = false;
+            
+            // Forward propagation is near-optimal for both topologically-sorted and
+            // DFS-sorted code.
+            propagateForward();
+            if (!m_changed)
+                break;
+            
+            // Backward propagation reduces the likelihood that pathological code will
+            // cause slowness. Loops (especially nested ones) resemble backward flow.
+            // This pass captures two cases: (1) it detects if the forward fixpoint
+            // found a sound solution and (2) short-circuits backward flow.
+            m_changed = false;
+            propagateBackward();
+        } while (m_changed);
+        
+        // 2) repropagate predictions while doing double voting.
+
+        do {
+            m_changed = false;
+            doRoundOfDoubleVoting();
+            propagateForward();
+            if (!m_changed)
+                break;
+            
+            m_changed = false;
+            doRoundOfDoubleVoting();
+            propagateBackward();
+        } while (m_changed);
+    }
+    
+private:
+    bool setPrediction(PredictedType prediction)
+    {
+        ASSERT(m_graph[m_compileIndex].hasResult());
+        
+        // setPrediction() is used when we know that there is no way that we can change
+        // our minds about what the prediction is going to be. There is no semantic
+        // difference between setPrediction() and mergePrediction() other than the
+        // increased checking to validate this property.
+        ASSERT(m_graph[m_compileIndex].prediction() == PredictNone || m_graph[m_compileIndex].prediction() == prediction);
+        
+        return m_graph[m_compileIndex].predict(prediction);
+    }
+    
+    bool mergePrediction(PredictedType prediction)
+    {
+        ASSERT(m_graph[m_compileIndex].hasResult());
+        
+        return m_graph[m_compileIndex].predict(prediction);
+    }
+    
+    bool isNotNegZero(NodeIndex nodeIndex)
+    {
+        if (!m_graph.isNumberConstant(nodeIndex))
+            return false;
+        double value = m_graph.valueOfNumberConstant(nodeIndex);
+        return !value && 1.0 / value < 0.0;
+    }
+    
+    bool isNotZero(NodeIndex nodeIndex)
+    {
+        if (!m_graph.isNumberConstant(nodeIndex))
+            return false;
+        return !!m_graph.valueOfNumberConstant(nodeIndex);
+    }
+    
+    void propagate(Node& node)
+    {
+        if (!node.shouldGenerate())
+            return;
+        
+        NodeType op = node.op();
+        NodeFlags flags = node.flags() & NodeBackPropMask;
+
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+        dataLog("   %s @%u: %s ", Graph::opName(op), m_compileIndex, nodeFlagsAsString(flags));
+#endif
+        
+        bool changed = false;
+        
+        switch (op) {
+        case JSConstant:
+        case WeakJSConstant: {
+            changed |= setPrediction(predictionFromValue(m_graph.valueOfJSConstant(m_compileIndex)));
+            break;
+        }
+            
+        case GetLocal: {
+            VariableAccessData* variableAccessData = node.variableAccessData();
+            PredictedType prediction = variableAccessData->prediction();
+            if (prediction)
+                changed |= mergePrediction(prediction);
+            
+            changed |= variableAccessData->mergeFlags(flags);
+            break;
+        }
+            
+        case SetLocal: {
+            VariableAccessData* variableAccessData = node.variableAccessData();
+            changed |= variableAccessData->predict(m_graph[node.child1()].prediction());
+            changed |= m_graph[node.child1()].mergeFlags(variableAccessData->flags());
+            break;
+        }
+            
+        case Flush: {
+            // Make sure that the analysis knows that flushed locals escape.
+            VariableAccessData* variableAccessData = node.variableAccessData();
+            changed |= variableAccessData->mergeFlags(NodeUsedAsValue);
+            break;
+        }
+            
+        case BitAnd:
+        case BitOr:
+        case BitXor:
+        case BitRShift:
+        case BitLShift:
+        case BitURShift: {
+            changed |= setPrediction(PredictInt32);
+            flags |= NodeUsedAsInt;
+            flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero);
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            changed |= m_graph[node.child2()].mergeFlags(flags);
+            break;
+        }
+            
+        case ValueToInt32: {
+            changed |= setPrediction(PredictInt32);
+            flags |= NodeUsedAsInt;
+            flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero);
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            break;
+        }
+            
+        case ArrayPop: {
+            changed |= mergePrediction(node.getHeapPrediction());
+            changed |= mergeDefaultFlags(node);
+            break;
+        }
+
+        case ArrayPush: {
+            changed |= mergePrediction(node.getHeapPrediction());
+            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
+            changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsValue);
+            break;
+        }
+
+        case RegExpExec:
+        case RegExpTest: {
+            changed |= mergePrediction(node.getHeapPrediction());
+            changed |= mergeDefaultFlags(node);
+            break;
+        }
+
+        case StringCharCodeAt: {
+            changed |= mergePrediction(PredictInt32);
+            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
+            changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+            break;
+        }
+
+        case ArithMod: {
+            PredictedType left = m_graph[node.child1()].prediction();
+            PredictedType right = m_graph[node.child2()].prediction();
+            
+            if (left && right) {
+                if (isInt32Prediction(mergePredictions(left, right))
+                    && nodeCanSpeculateInteger(node.arithNodeFlags()))
+                    changed |= mergePrediction(PredictInt32);
+                else
+                    changed |= mergePrediction(PredictDouble);
+            }
+            
+            flags |= NodeUsedAsValue;
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            changed |= m_graph[node.child2()].mergeFlags(flags);
+            break;
+        }
+            
+        case UInt32ToNumber: {
+            if (nodeCanSpeculateInteger(node.arithNodeFlags()))
+                changed |= mergePrediction(PredictInt32);
+            else
+                changed |= mergePrediction(PredictNumber);
+            
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            break;
+        }
+
+        case ValueAdd: {
+            PredictedType left = m_graph[node.child1()].prediction();
+            PredictedType right = m_graph[node.child2()].prediction();
+            
+            if (left && right) {
+                if (isNumberPrediction(left) && isNumberPrediction(right)) {
+                    if (m_graph.addShouldSpeculateInteger(node))
+                        changed |= mergePrediction(PredictInt32);
+                    else
+                        changed |= mergePrediction(PredictDouble);
+                } else if (!(left & PredictNumber) || !(right & PredictNumber)) {
+                    // left or right is definitely something other than a number.
+                    changed |= mergePrediction(PredictString);
+                } else
+                    changed |= mergePrediction(PredictString | PredictInt32 | PredictDouble);
+            }
+            
+            if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index()))
+                flags &= ~NodeNeedsNegZero;
+            
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            changed |= m_graph[node.child2()].mergeFlags(flags);
+            break;
+        }
+            
+        case ArithAdd: {
+            PredictedType left = m_graph[node.child1()].prediction();
+            PredictedType right = m_graph[node.child2()].prediction();
+            
+            if (left && right) {
+                if (m_graph.addShouldSpeculateInteger(node))
+                    changed |= mergePrediction(PredictInt32);
+                else
+                    changed |= mergePrediction(PredictDouble);
+            }
+            
+            if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index()))
+                flags &= ~NodeNeedsNegZero;
+            
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            changed |= m_graph[node.child2()].mergeFlags(flags);
+            break;
+        }
+            
+        case ArithSub: {
+            PredictedType left = m_graph[node.child1()].prediction();
+            PredictedType right = m_graph[node.child2()].prediction();
+            
+            if (left && right) {
+                if (m_graph.addShouldSpeculateInteger(node))
+                    changed |= mergePrediction(PredictInt32);
+                else
+                    changed |= mergePrediction(PredictDouble);
+            }
+
+            if (isNotZero(node.child1().index()) || isNotZero(node.child2().index()))
+                flags &= ~NodeNeedsNegZero;
+            
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            changed |= m_graph[node.child2()].mergeFlags(flags);
+            break;
+        }
+            
+        case ArithNegate:
+            if (m_graph[node.child1()].prediction()) {
+                if (m_graph.negateShouldSpeculateInteger(node))
+                    changed |= mergePrediction(PredictInt32);
+                else
+                    changed |= mergePrediction(PredictDouble);
+            }
+
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            break;
+            
+        case ArithMin:
+        case ArithMax: {
+            PredictedType left = m_graph[node.child1()].prediction();
+            PredictedType right = m_graph[node.child2()].prediction();
+            
+            if (left && right) {
+                if (isInt32Prediction(mergePredictions(left, right))
+                    && nodeCanSpeculateInteger(node.arithNodeFlags()))
+                    changed |= mergePrediction(PredictInt32);
+                else
+                    changed |= mergePrediction(PredictDouble);
+            }
+
+            flags |= NodeUsedAsNumber;
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            changed |= m_graph[node.child2()].mergeFlags(flags);
+            break;
+        }
+            
+        case ArithMul:
+        case ArithDiv: {
+            PredictedType left = m_graph[node.child1()].prediction();
+            PredictedType right = m_graph[node.child2()].prediction();
+            
+            if (left && right) {
+                if (isInt32Prediction(mergePredictions(left, right))
+                    && nodeCanSpeculateInteger(node.arithNodeFlags()))
+                    changed |= mergePrediction(PredictInt32);
+                else
+                    changed |= mergePrediction(PredictDouble);
+            }
+
+            // As soon as a multiply happens, we can easily end up in the part
+            // of the double domain where the point at which you do truncation
+            // can change the outcome. So, ArithMul always checks for overflow
+            // no matter what, and always forces its inputs to check as well.
+            
+            flags |= NodeUsedAsNumber | NodeNeedsNegZero;
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            changed |= m_graph[node.child2()].mergeFlags(flags);
+            break;
+        }
+            
+        case ArithSqrt: {
+            changed |= setPrediction(PredictDouble);
+            changed |= m_graph[node.child1()].mergeFlags(flags | NodeUsedAsValue);
+            break;
+        }
+            
+        case ArithAbs: {
+            PredictedType child = m_graph[node.child1()].prediction();
+            if (nodeCanSpeculateInteger(node.arithNodeFlags()))
+                changed |= mergePrediction(child);
+            else
+                changed |= setPrediction(PredictDouble);
+
+            flags &= ~NodeNeedsNegZero;
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            break;
+        }
+            
+        case LogicalNot:
+        case CompareLess:
+        case CompareLessEq:
+        case CompareGreater:
+        case CompareGreaterEq:
+        case CompareEq:
+        case CompareStrictEq:
+        case InstanceOf:
+        case IsUndefined:
+        case IsBoolean:
+        case IsNumber:
+        case IsString:
+        case IsObject:
+        case IsFunction: {
+            changed |= setPrediction(PredictBoolean);
+            changed |= mergeDefaultFlags(node);
+            break;
+        }
+            
+        case GetById: {
+            changed |= mergePrediction(node.getHeapPrediction());
+            changed |= mergeDefaultFlags(node);
+            break;
+        }
+            
+        case GetByIdFlush:
+            changed |= mergePrediction(node.getHeapPrediction());
+            changed |= mergeDefaultFlags(node);
+            break;
+            
+        case GetByVal: {
+            if (m_graph[node.child1()].shouldSpeculateFloat32Array()
+                || m_graph[node.child1()].shouldSpeculateFloat64Array())
+                changed |= mergePrediction(PredictDouble);
+            else
+                changed |= mergePrediction(node.getHeapPrediction());
+
+            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
+            changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+            break;
+        }
+            
+        case GetPropertyStorage: 
+        case GetIndexedPropertyStorage: {
+            changed |= setPrediction(PredictOther);
+            changed |= mergeDefaultFlags(node);
+            break;
+        }
+
+        case GetByOffset: {
+            changed |= mergePrediction(node.getHeapPrediction());
+            changed |= mergeDefaultFlags(node);
+            break;
+        }
+            
+        case Call:
+        case Construct: {
+            changed |= mergePrediction(node.getHeapPrediction());
+            for (unsigned childIdx = node.firstChild();
+                 childIdx < node.firstChild() + node.numChildren();
+                 ++childIdx) {
+                Edge edge = m_graph.m_varArgChildren[childIdx];
+                changed |= m_graph[edge].mergeFlags(NodeUsedAsValue);
+            }
+            break;
+        }
+            
+        case ConvertThis: {
+            PredictedType prediction = m_graph[node.child1()].prediction();
+            if (prediction) {
+                if (prediction & ~PredictObjectMask) {
+                    prediction &= PredictObjectMask;
+                    prediction = mergePredictions(prediction, PredictObjectOther);
+                }
+                changed |= mergePrediction(prediction);
+            }
+            changed |= mergeDefaultFlags(node);
+            break;
+        }
+            
+        case GetGlobalVar: {
+            changed |= mergePrediction(node.getHeapPrediction());
+            break;
+        }
+            
+        case PutGlobalVar: {
+            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
+            break;
+        }
+            
+        case GetScopedVar:
+        case Resolve:
+        case ResolveBase:
+        case ResolveBaseStrictPut:
+        case ResolveGlobal: {
+            PredictedType prediction = node.getHeapPrediction();
+            changed |= mergePrediction(prediction);
+            break;
+        }
+            
+        case GetScopeChain: {
+            changed |= setPrediction(PredictCellOther);
+            break;
+        }
+            
+        case GetCallee: {
+            changed |= setPrediction(PredictFunction);
+            break;
+        }
+            
+        case CreateThis:
+        case NewObject: {
+            changed |= setPrediction(PredictFinalObject);
+            changed |= mergeDefaultFlags(node);
+            break;
+        }
+            
+        case NewArray: {
+            changed |= setPrediction(PredictArray);
+            for (unsigned childIdx = node.firstChild();
+                 childIdx < node.firstChild() + node.numChildren();
+                 ++childIdx) {
+                Edge edge = m_graph.m_varArgChildren[childIdx];
+                changed |= m_graph[edge].mergeFlags(NodeUsedAsValue);
+            }
+            break;
+        }
+            
+        case NewArrayBuffer: {
+            changed |= setPrediction(PredictArray);
+            break;
+        }
+            
+        case NewRegexp: {
+            changed |= setPrediction(PredictObjectOther);
+            break;
+        }
+        
+        case StringCharAt: {
+            changed |= setPrediction(PredictString);
+            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
+            changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+            break;
+        }
+            
+        case StrCat: {
+            changed |= setPrediction(PredictString);
+            for (unsigned childIdx = node.firstChild();
+                 childIdx < node.firstChild() + node.numChildren();
+                 ++childIdx)
+                changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsNumber);
+            break;
+        }
+            
+        case ToPrimitive: {
+            PredictedType child = m_graph[node.child1()].prediction();
+            if (child) {
+                if (isObjectPrediction(child)) {
+                    // I'd love to fold this case into the case below, but I can't, because
+                    // removing PredictObjectMask from something that only has an object
+                    // prediction and nothing else means we have an ill-formed PredictedType
+                    // (strong predict-none). This should be killed once we remove all traces
+                    // of static (aka weak) predictions.
+                    changed |= mergePrediction(PredictString);
+                } else if (child & PredictObjectMask) {
+                    // Objects get turned into strings. So if the input has hints of objectness,
+                    // the output will have hinsts of stringiness.
+                    changed |= mergePrediction(
+                        mergePredictions(child & ~PredictObjectMask, PredictString));
+                } else
+                    changed |= mergePrediction(child);
+            }
+            changed |= m_graph[node.child1()].mergeFlags(flags);
+            break;
+        }
+            
+        case CreateActivation: {
+            changed |= setPrediction(PredictObjectOther);
+            break;
+        }
+            
+        case NewFunction:
+        case NewFunctionNoCheck:
+        case NewFunctionExpression: {
+            changed |= setPrediction(PredictFunction);
+            break;
+        }
+            
+        case PutByValAlias:
+        case GetArrayLength:
+        case GetInt8ArrayLength:
+        case GetInt16ArrayLength:
+        case GetInt32ArrayLength:
+        case GetUint8ArrayLength:
+        case GetUint8ClampedArrayLength:
+        case GetUint16ArrayLength:
+        case GetUint32ArrayLength:
+        case GetFloat32ArrayLength:
+        case GetFloat64ArrayLength:
+        case GetStringLength:
+        case Int32ToDouble:
+        case DoubleAsInt32: {
+            // This node should never be visible at this stage of compilation. It is
+            // inserted by fixup(), which follows this phase.
+            ASSERT_NOT_REACHED();
+            break;
+        }
+        
+        case PutByVal:
+            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
+            changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+            changed |= m_graph[node.child3()].mergeFlags(NodeUsedAsValue);
+            break;
+
+        case PutScopedVar:
+        case Return:
+        case Throw:
+            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
+            break;
+
+        case PutById:
+        case PutByIdDirect:
+            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
+            changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsValue);
+            break;
+
+        case PutByOffset:
+            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
+            changed |= m_graph[node.child3()].mergeFlags(NodeUsedAsValue);
+            break;
+            
+        case Phi:
+            break;
+
+#ifndef NDEBUG
+        // These get ignored because they don't return anything.
+        case DFG::Jump:
+        case Branch:
+        case Breakpoint:
+        case CheckHasInstance:
+        case ThrowReferenceError:
+        case ForceOSRExit:
+        case SetArgument:
+        case CheckStructure:
+        case CheckFunction:
+        case PutStructure:
+        case TearOffActivation:
+        case CheckNumber:
+            changed |= mergeDefaultFlags(node);
+            break;
+            
+        // These gets ignored because it doesn't do anything.
+        case Phantom:
+        case InlineStart:
+        case Nop:
+            break;
+            
+        case LastNodeType:
+            ASSERT_NOT_REACHED();
+            break;
+#else
+        default:
+            changed |= mergeDefaultFlags(node);
+            break;
+#endif
+        }
+
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+        dataLog("%s\n", predictionToString(m_graph[m_compileIndex].prediction()));
+#endif
+        
+        m_changed |= changed;
+    }
+        
+    bool mergeDefaultFlags(Node& node)
+    {
+        bool changed = false;
+        if (node.flags() & NodeHasVarArgs) {
+            for (unsigned childIdx = node.firstChild();
+                 childIdx < node.firstChild() + node.numChildren();
+                 childIdx++)
+                changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsValue);
+        } else {
+            if (!node.child1())
+                return changed;
+            changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
+            if (!node.child2())
+                return changed;
+            changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsValue);
+            if (!node.child3())
+                return changed;
+            changed |= m_graph[node.child3()].mergeFlags(NodeUsedAsValue);
+        }
+        return changed;
+    }
+    
+    void propagateForward()
+    {
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+        dataLog("Propagating predictions forward [%u]\n", ++m_count);
+#endif
+        for (m_compileIndex = 0; m_compileIndex < m_graph.size(); ++m_compileIndex)
+            propagate(m_graph[m_compileIndex]);
+    }
+    
+    void propagateBackward()
+    {
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+        dataLog("Propagating predictions backward [%u]\n", ++m_count);
+#endif
+        for (m_compileIndex = m_graph.size(); m_compileIndex-- > 0;)
+            propagate(m_graph[m_compileIndex]);
+    }
+    
+    void vote(Edge nodeUse, VariableAccessData::Ballot ballot)
+    {
+        switch (m_graph[nodeUse].op()) {
+        case ValueToInt32:
+        case UInt32ToNumber:
+            nodeUse = m_graph[nodeUse].child1();
+            break;
+        default:
+            break;
+        }
+        
+        if (m_graph[nodeUse].op() == GetLocal)
+            m_graph[nodeUse].variableAccessData()->vote(ballot);
+    }
+    
+    void vote(Node& node, VariableAccessData::Ballot ballot)
+    {
+        if (node.flags() & NodeHasVarArgs) {
+            for (unsigned childIdx = node.firstChild();
+                 childIdx < node.firstChild() + node.numChildren();
+                 childIdx++)
+                vote(m_graph.m_varArgChildren[childIdx], ballot);
+            return;
+        }
+        
+        if (!node.child1())
+            return;
+        vote(node.child1(), ballot);
+        if (!node.child2())
+            return;
+        vote(node.child2(), ballot);
+        if (!node.child3())
+            return;
+        vote(node.child3(), ballot);
+    }
+    
+    void doRoundOfDoubleVoting()
+    {
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+        dataLog("Voting on double uses of locals [%u]\n", m_count);
+#endif
+        for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i)
+            m_graph.m_variableAccessData[i].find()->clearVotes();
+        for (m_compileIndex = 0; m_compileIndex < m_graph.size(); ++m_compileIndex) {
+            Node& node = m_graph[m_compileIndex];
+            switch (node.op()) {
+            case ValueAdd:
+            case ArithAdd:
+            case ArithSub: {
+                PredictedType left = m_graph[node.child1()].prediction();
+                PredictedType right = m_graph[node.child2()].prediction();
+                
+                VariableAccessData::Ballot ballot;
+                
+                if (isNumberPrediction(left) && isNumberPrediction(right)
+                    && !m_graph.addShouldSpeculateInteger(node))
+                    ballot = VariableAccessData::VoteDouble;
+                else
+                    ballot = VariableAccessData::VoteValue;
+                
+                vote(node.child1(), ballot);
+                vote(node.child2(), ballot);
+                break;
+            }
+                
+            case ArithMul:
+            case ArithMin:
+            case ArithMax:
+            case ArithMod:
+            case ArithDiv: {
+                PredictedType left = m_graph[node.child1()].prediction();
+                PredictedType right = m_graph[node.child2()].prediction();
+                
+                VariableAccessData::Ballot ballot;
+                
+                if (isNumberPrediction(left) && isNumberPrediction(right)
+                    && !(Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child1()])
+                         && node.canSpeculateInteger()))
+                    ballot = VariableAccessData::VoteDouble;
+                else
+                    ballot = VariableAccessData::VoteValue;
+                
+                vote(node.child1(), ballot);
+                vote(node.child2(), ballot);
+                break;
+            }
+                
+            case ArithAbs:
+                VariableAccessData::Ballot ballot;
+                if (!(m_graph[node.child1()].shouldSpeculateInteger()
+                      && node.canSpeculateInteger()))
+                    ballot = VariableAccessData::VoteDouble;
+                else
+                    ballot = VariableAccessData::VoteValue;
+                
+                vote(node.child1(), ballot);
+                break;
+                
+            case ArithSqrt:
+                vote(node.child1(), VariableAccessData::VoteDouble);
+                break;
+                
+            case SetLocal: {
+                PredictedType prediction = m_graph[node.child1()].prediction();
+                if (isDoublePrediction(prediction))
+                    node.variableAccessData()->vote(VariableAccessData::VoteDouble);
+                else if (!isNumberPrediction(prediction) || isInt32Prediction(prediction))
+                    node.variableAccessData()->vote(VariableAccessData::VoteValue);
+                break;
+            }
+                
+            default:
+                vote(node, VariableAccessData::VoteValue);
+                break;
+            }
+        }
+        for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
+            VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
+            if (!variableAccessData->isRoot())
+                continue;
+            if (operandIsArgument(variableAccessData->local())
+                || m_graph.isCaptured(variableAccessData->local()))
+                continue;
+            m_changed |= variableAccessData->tallyVotesForShouldUseDoubleFormat();
+        }
+        for (unsigned i = 0; i < m_graph.m_argumentPositions.size(); ++i)
+            m_changed |= m_graph.m_argumentPositions[i].mergeArgumentAwareness();
+        for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
+            VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
+            if (!variableAccessData->isRoot())
+                continue;
+            if (operandIsArgument(variableAccessData->local())
+                || m_graph.isCaptured(variableAccessData->local()))
+                continue;
+            m_changed |= variableAccessData->makePredictionForDoubleFormat();
+        }
+    }
+    
+    NodeIndex m_compileIndex;
+    bool m_changed;
+
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+    unsigned m_count;
+#endif
+};
+    
+void performPredictionPropagation(Graph& graph)
+{
+    runPhase<PredictionPropagationPhase>(graph);
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+