/*
 * Copyright (C) 2011-2017 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"
#include "JSCInlines.h"

namespace JSC { namespace DFG {

namespace {

bool verboseFixPointLoops = false;

class PredictionPropagationPhase : public Phase {
public:
    PredictionPropagationPhase(Graph& graph)
        : Phase(graph, "prediction propagation")
    {
    }

    bool run()
    {
        ASSERT(m_graph.m_form == ThreadedCPS);
        ASSERT(m_graph.m_unificationState == GloballyUnified);

        m_pass = PrimaryPass;

        propagateThroughArgumentPositions();

        processInvariants();

        propagateToFixpoint();

        m_pass = RareCasePass;
        propagateToFixpoint();

        m_pass = DoubleVotingPass;
        unsigned counter = 0;
        do {
            if (verboseFixPointLoops)
                ++counter;

            m_changed = false;
            doRoundOfDoubleVoting();
            if (!m_changed)
                break;
            m_changed = false;
            propagateForward();
        } while (m_changed);

        if (verboseFixPointLoops)
            dataLog("Iterated ", counter, " times in double voting fixpoint.\n");

        return true;
    }

private:
    void propagateToFixpoint()
    {
        unsigned counter = 0;
        do {
            if (verboseFixPointLoops)
                ++counter;

            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);

        if (verboseFixPointLoops)
            dataLog("Iterated ", counter, " times in propagateToFixpoint.\n");
    }

    bool setPrediction(SpeculatedType prediction)
    {
        ASSERT(m_currentNode->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 mergeSpeculation() other than the
        // increased checking to validate this property.
        ASSERT(m_currentNode->prediction() == SpecNone || m_currentNode->prediction() == prediction);

        return m_currentNode->predict(prediction);
    }

    bool mergePrediction(SpeculatedType prediction)
    {
        ASSERT(m_currentNode->hasResult());

        return m_currentNode->predict(prediction);
    }

    SpeculatedType speculatedDoubleTypeForPrediction(SpeculatedType value)
    {
        SpeculatedType result = SpecDoubleReal;
        if (value & SpecDoubleImpureNaN)
            result |= SpecDoubleImpureNaN;
        if (value & SpecDoublePureNaN)
            result |= SpecDoublePureNaN;
        if (!isFullNumberOrBooleanSpeculation(value))
            result |= SpecDoublePureNaN;
        return result;
    }

    SpeculatedType speculatedDoubleTypeForPredictions(SpeculatedType left, SpeculatedType right)
    {
        return speculatedDoubleTypeForPrediction(mergeSpeculations(left, right));
    }

    void propagate(Node* node)
    {
        NodeType op = node->op();

        bool changed = false;

        switch (op) {
        case GetLocal: {
            VariableAccessData* variable = node->variableAccessData();
            SpeculatedType prediction = variable->prediction();
            if (!variable->couldRepresentInt52() && (prediction & SpecNonInt32AsInt52))
                prediction = (prediction | SpecAnyIntAsDouble) & ~SpecNonInt32AsInt52;
            if (prediction)
                changed |= mergePrediction(prediction);
            break;
        }

        case SetLocal: {
            VariableAccessData* variableAccessData = node->variableAccessData();
            changed |= variableAccessData->predict(node->child1()->prediction());
            break;
        }

        case UInt32ToNumber: {
            if (node->canSpeculateInt32(m_pass))
                changed |= mergePrediction(SpecInt32Only);
            else if (enableInt52())
                changed |= mergePrediction(SpecInt52Any);
            else
                changed |= mergePrediction(SpecBytecodeNumber);
            break;
        }

        case ValueBitLShift: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            if (left && right) {
                if (isBigIntSpeculation(left) && isBigIntSpeculation(right))
                    changed |= mergePrediction(SpecBigInt);
                else if (isFullNumberOrBooleanSpeculationExpectingDefined(left) && isFullNumberOrBooleanSpeculationExpectingDefined(right))
                    changed |= mergePrediction(SpecInt32Only);
                else
                    changed |= mergePrediction(node->getHeapPrediction());
            }

            break;
        }

        case ValueAdd: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            if (left && right) {
                if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
                    && isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
                    if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
                        changed |= mergePrediction(SpecInt32Only);
                    else if (m_graph.addShouldSpeculateInt52(node))
                        changed |= mergePrediction(SpecInt52Any);
                    else
                        changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
                } else if (isStringOrStringObjectSpeculation(left) || isStringOrStringObjectSpeculation(right)) {
                    // left or right is definitely something other than a number.
                    changed |= mergePrediction(SpecString);
                } else if (isBigIntSpeculation(left) && isBigIntSpeculation(right))
                    changed |= mergePrediction(SpecBigInt);
                else {
                    changed |= mergePrediction(SpecInt32Only);
                    if (node->mayHaveDoubleResult())
                        changed |= mergePrediction(SpecBytecodeDouble);
                    if (node->mayHaveBigIntResult())
                        changed |= mergePrediction(SpecBigInt);
                    if (node->mayHaveNonNumericResult())
                        changed |= mergePrediction(SpecString);
                }
            }
            break;
        }

        case ArithAdd: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            if (left && right) {
                if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
                    changed |= mergePrediction(SpecInt32Only);
                else if (m_graph.addShouldSpeculateInt52(node))
                    changed |= mergePrediction(SpecInt52Any);
                else if (isFullNumberOrBooleanSpeculation(left) && isFullNumberOrBooleanSpeculation(right))
                    changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
                else if (node->mayHaveNonIntResult() || (left & SpecBytecodeDouble) || (right & SpecBytecodeDouble))
                    changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
                else
                    changed |= mergePrediction(SpecInt32Only);
            }
            break;
        }

        case ArithSub: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            if (left && right) {
                if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
                    && isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
                    if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
                        changed |= mergePrediction(SpecInt32Only);
                    else if (m_graph.addShouldSpeculateInt52(node))
                        changed |= mergePrediction(SpecInt52Any);
                    else
                        changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
                } else if (node->mayHaveNonIntResult() || (left & SpecBytecodeDouble) || (right & SpecBytecodeDouble))
                    changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
                else
                    changed |= mergePrediction(SpecInt32Only);
            }
            break;
        }

        case ValueSub: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            if (left && right) {
                if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
                    && isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
                    if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
                        changed |= mergePrediction(SpecInt32Only);
                    else if (m_graph.addShouldSpeculateInt52(node))
                        changed |= mergePrediction(SpecInt52Any);
                    else
                        changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
                } else if (isBigIntSpeculation(left) && isBigIntSpeculation(right))
                    changed |= mergePrediction(SpecBigInt);
                else {
                    changed |= mergePrediction(SpecInt32Only);
                    if (node->mayHaveDoubleResult())
                        changed |= mergePrediction(SpecBytecodeDouble);
                    if (node->mayHaveBigIntResult())
                        changed |= mergePrediction(SpecBigInt);
                }
            }

            break;
        }

        case ValuePow: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            if (left && right) {
                if (node->child1()->shouldSpeculateBigInt() && node->child2()->shouldSpeculateBigInt())
                    changed |= mergePrediction(SpecBigInt);
                else if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
                    && isFullNumberOrBooleanSpeculationExpectingDefined(right))
                    changed |= mergePrediction(SpecBytecodeDouble);
                else
                    changed |= mergePrediction(SpecBytecodeDouble | SpecBigInt);
            }
            break;
        }

        case ValueNegate:
        case ArithNegate: {
            SpeculatedType prediction = node->child1()->prediction();
            if (prediction) {
                if (isInt32OrBooleanSpeculation(prediction) && node->canSpeculateInt32(m_pass))
                    changed |= mergePrediction(SpecInt32Only);
                else if (m_graph.unaryArithShouldSpeculateInt52(node, m_pass))
                    changed |= mergePrediction(SpecInt52Any);
                else if (isBytecodeNumberSpeculation(prediction))
                    changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction()));
                else {
                    changed |= mergePrediction(SpecInt32Only);
                    if (node->op() == ValueNegate && node->mayHaveBigIntResult())
                        changed |= mergePrediction(SpecBigInt);
                    if (node->mayHaveDoubleResult())
                        changed |= mergePrediction(SpecBytecodeDouble);
                }
            }
            break;
        }
        case ArithMin:
        case ArithMax: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            if (left && right) {
                if (Node::shouldSpeculateInt32OrBooleanForArithmetic(node->child1().node(), node->child2().node())
                    && node->canSpeculateInt32(m_pass))
                    changed |= mergePrediction(SpecInt32Only);
                else
                    changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
            }
            break;
        }

        case ValueMul:
        case ArithMul: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            if (left && right) {
                // FIXME: We're currently relying on prediction propagation and backwards propagation
                // whenever we can, and only falling back on result flags if that fails. And the result
                // flags logic doesn't know how to use backwards propagation. We should get rid of the
                // prediction propagation logic and rely solely on the result type.
                if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
                    && isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
                    if (m_graph.binaryArithShouldSpeculateInt32(node, m_pass))
                        changed |= mergePrediction(SpecInt32Only);
                    else if (m_graph.binaryArithShouldSpeculateInt52(node, m_pass))
                        changed |= mergePrediction(SpecInt52Any);
                    else
                        changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
                } else if (op == ValueMul && isBigIntSpeculation(left) && isBigIntSpeculation(right))
                    changed |= mergePrediction(SpecBigInt);
                else {
                    changed |= mergePrediction(SpecInt32Only);
                    if (node->mayHaveDoubleResult()
                        || (left & SpecBytecodeDouble)
                        || (right & SpecBytecodeDouble))
                        changed |= mergePrediction(SpecBytecodeDouble);
                    if ((op == ValueMul && node->mayHaveBigIntResult())
                        || (left & SpecBigInt)
                        || (right & SpecBigInt))
                        changed |= mergePrediction(SpecBigInt);
                }
            }
            break;
        }

        case ValueDiv:
        case ValueMod:
        case ArithDiv:
        case ArithMod: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            if (left && right) {
                if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
                    && isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
                    if (m_graph.binaryArithShouldSpeculateInt32(node, m_pass))
                        changed |= mergePrediction(SpecInt32Only);
                    else
                        changed |= mergePrediction(SpecBytecodeDouble);
                } else if ((op == ValueDiv || op == ValueMod) && isBigIntSpeculation(left) && isBigIntSpeculation(right))
                    changed |= mergePrediction(SpecBigInt);
                else {
                    changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
                    if ((op == ValueDiv || op == ValueMod) && (node->mayHaveBigIntResult()
                        || (left & SpecBigInt)
                        || (right & SpecBigInt)))
                        changed |= mergePrediction(SpecBigInt);
                }
            }
            break;
        }

        case ArithAbs: {
            SpeculatedType childPrediction = node->child1()->prediction();
            if (isInt32OrBooleanSpeculation(childPrediction)
                && node->canSpeculateInt32(m_pass))
                changed |= mergePrediction(SpecInt32Only);
            else
                changed |= mergePrediction(SpecBytecodeDouble);
            break;
        }

        case GetByVal:
        case AtomicsAdd:
        case AtomicsAnd:
        case AtomicsCompareExchange:
        case AtomicsExchange:
        case AtomicsLoad:
        case AtomicsOr:
        case AtomicsStore:
        case AtomicsSub:
        case AtomicsXor: {
            Edge child1 = m_graph.child(node, 0);
            if (!child1->prediction())
                break;

            Edge child2 = m_graph.child(node, 1);
            ArrayMode arrayMode = node->arrayMode().refine(
                m_graph, node,
                child1->prediction(),
                child2->prediction(),
                SpecNone);

            switch (arrayMode.type()) {
            case Array::Int32:
                if (arrayMode.isOutOfBounds())
                    changed |= mergePrediction(node->getHeapPrediction() | SpecInt32Only);
                else
                    changed |= mergePrediction(SpecInt32Only);
                break;
            case Array::Double:
                if (arrayMode.isOutOfBounds())
                    changed |= mergePrediction(node->getHeapPrediction() | SpecDoubleReal);
                else if (node->getHeapPrediction() & SpecNonIntAsDouble)
                    changed |= mergePrediction(SpecDoubleReal);
                else
                    changed |= mergePrediction(SpecAnyIntAsDouble);
                break;
            case Array::Float32Array:
            case Array::Float64Array:
                changed |= mergePrediction(SpecFullDouble);
                break;
            case Array::Uint32Array:
                if (isInt32SpeculationForArithmetic(node->getHeapPrediction()) && node->op() == GetByVal)
                    changed |= mergePrediction(SpecInt32Only);
                else if (enableInt52())
                    changed |= mergePrediction(SpecInt52Any);
                else
                    changed |= mergePrediction(SpecInt32Only | SpecAnyIntAsDouble);
                break;
            case Array::Int8Array:
            case Array::Uint8Array:
            case Array::Int16Array:
            case Array::Uint16Array:
            case Array::Int32Array:
                changed |= mergePrediction(SpecInt32Only);
                break;
            default:
                changed |= mergePrediction(node->getHeapPrediction());
                break;
            }
            break;
        }

        case ToThis: {
            // ToThis in methods for primitive types should speculate primitive types in strict mode.
            bool isStrictMode = m_graph.isStrictModeFor(node->origin.semantic);
            if (isStrictMode) {
                if (node->child1()->shouldSpeculateBoolean()) {
                    changed |= mergePrediction(SpecBoolean);
                    break;
                }

                if (node->child1()->shouldSpeculateInt32()) {
                    changed |= mergePrediction(SpecInt32Only);
                    break;
                }

                if (node->child1()->shouldSpeculateInt52()) {
                    changed |= mergePrediction(SpecInt52Any);
                    break;
                }

                if (node->child1()->shouldSpeculateNumber()) {
                    changed |= mergePrediction(SpecBytecodeNumber);
                    break;
                }

                if (node->child1()->shouldSpeculateSymbol()) {
                    changed |= mergePrediction(SpecSymbol);
                    break;
                }

                if (node->child1()->shouldSpeculateBigInt()) {
                    changed |= mergePrediction(SpecBigInt);
                    break;
                }

                if (node->child1()->shouldSpeculateStringIdent()) {
                    changed |= mergePrediction(SpecStringIdent);
                    break;
                }

                if (node->child1()->shouldSpeculateString()) {
                    changed |= mergePrediction(SpecString);
                    break;
                }
            } else {
                if (node->child1()->shouldSpeculateString()) {
                    changed |= mergePrediction(SpecStringObject);
                    break;
                }
            }

            SpeculatedType prediction = node->child1()->prediction();
            if (isStrictMode)
                changed |= mergePrediction(node->getHeapPrediction());
            else if (prediction) {
                if (prediction & ~SpecObject) {
                    // Wrapper objects are created only in sloppy mode.
                    prediction &= SpecObject;
                    prediction = mergeSpeculations(prediction, SpecObjectOther);
                }
                changed |= mergePrediction(prediction);
            }
            break;
        }

        case ToPrimitive: {
            SpeculatedType child = node->child1()->prediction();
            if (child)
                changed |= mergePrediction(resultOfToPrimitive(child));
            break;
        }

        case NormalizeMapKey: {
            SpeculatedType prediction = node->child1()->prediction();
            if (prediction)
                changed |= mergePrediction(prediction);
            break;
        }

        default:
            break;
        }

        m_changed |= changed;
    }

    void propagateForward()
    {
        for (Node* node : m_dependentNodes) {
            m_currentNode = node;
            propagate(m_currentNode);
        }
    }

    void propagateBackward()
    {
        for (unsigned i = m_dependentNodes.size(); i--;) {
            m_currentNode = m_dependentNodes[i];
            propagate(m_currentNode);
        }
    }

    void doDoubleVoting(Node* node, float weight)
    {
        // Loop pre-headers created by OSR entrypoint creation may have NaN weight to indicate
        // that we actually don't know they weight. Assume that they execute once. This turns
        // out to be an OK assumption since the pre-header doesn't have any meaningful code.
        if (weight != weight)
            weight = 1;

        switch (node->op()) {
        case ValueAdd:
        case ValueSub:
        case ArithAdd:
        case ArithSub: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            DoubleBallot ballot;

            if (isFullNumberSpeculation(left)
                && isFullNumberSpeculation(right)
                && !m_graph.addShouldSpeculateInt32(node, m_pass)
                && !m_graph.addShouldSpeculateInt52(node))
                ballot = VoteDouble;
            else
                ballot = VoteValue;

            m_graph.voteNode(node->child1(), ballot, weight);
            m_graph.voteNode(node->child2(), ballot, weight);
            break;
        }

        case ValueMul:
        case ArithMul: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            DoubleBallot ballot;

            if (isFullNumberSpeculation(left)
                && isFullNumberSpeculation(right)
                && !m_graph.binaryArithShouldSpeculateInt32(node, m_pass)
                && !m_graph.binaryArithShouldSpeculateInt52(node, m_pass))
                ballot = VoteDouble;
            else
                ballot = VoteValue;

            m_graph.voteNode(node->child1(), ballot, weight);
            m_graph.voteNode(node->child2(), ballot, weight);
            break;
        }

        case ArithMin:
        case ArithMax:
        case ArithMod:
        case ValueDiv:
        case ValueMod:
        case ArithDiv: {
            SpeculatedType left = node->child1()->prediction();
            SpeculatedType right = node->child2()->prediction();

            DoubleBallot ballot;

            if (isFullNumberSpeculation(left)
                && isFullNumberSpeculation(right)
                && !m_graph.binaryArithShouldSpeculateInt32(node, m_pass))
                ballot = VoteDouble;
            else
                ballot = VoteValue;

            m_graph.voteNode(node->child1(), ballot, weight);
            m_graph.voteNode(node->child2(), ballot, weight);
            break;
        }

        case ArithAbs:
            DoubleBallot ballot;
            if (node->child1()->shouldSpeculateNumber()
                && !m_graph.unaryArithShouldSpeculateInt32(node, m_pass))
                ballot = VoteDouble;
            else
                ballot = VoteValue;

            m_graph.voteNode(node->child1(), ballot, weight);
            break;

        case ArithSqrt:
        case ArithUnary:
            if (node->child1()->shouldSpeculateNumber())
                m_graph.voteNode(node->child1(), VoteDouble, weight);
            else
                m_graph.voteNode(node->child1(), VoteValue, weight);
            break;

        case SetLocal: {
            SpeculatedType prediction = node->child1()->prediction();
            if (isDoubleSpeculation(prediction))
                node->variableAccessData()->vote(VoteDouble, weight);
            else if (!isFullNumberSpeculation(prediction) || isInt32OrInt52Speculation(prediction))
                node->variableAccessData()->vote(VoteValue, weight);
            break;
        }

        case PutByValDirect:
        case PutByVal:
        case PutByValAlias: {
            Edge child1 = m_graph.varArgChild(node, 0);
            Edge child2 = m_graph.varArgChild(node, 1);
            Edge child3 = m_graph.varArgChild(node, 2);
            m_graph.voteNode(child1, VoteValue, weight);
            m_graph.voteNode(child2, VoteValue, weight);
            switch (node->arrayMode().type()) {
            case Array::Double:
                m_graph.voteNode(child3, VoteDouble, weight);
                break;
            default:
                m_graph.voteNode(child3, VoteValue, weight);
                break;
            }
            break;
        }

        case DataViewSet: {
            DataViewData data = node->dataViewData();
            if (data.isFloatingPoint)
                m_graph.voteNode(m_graph.varArgChild(node, 2), VoteValue, weight);
            break;
        }

        case MovHint:
            // Ignore these since they have no effect on in-DFG execution.
            break;

        default:
            m_graph.voteChildren(node, VoteValue, weight);
            break;
        }
    }

    void doRoundOfDoubleVoting()
    {
        for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i)
            m_graph.m_variableAccessData[i].find()->clearVotes();
        for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
            BasicBlock* block = m_graph.block(blockIndex);
            if (!block)
                continue;
            ASSERT(block->isReachable);
            for (unsigned i = 0; i < block->size(); ++i) {
                m_currentNode = block->at(i);
                doDoubleVoting(m_currentNode, block->executionCount);
            }
        }
        for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
            VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
            if (!variableAccessData->isRoot())
                continue;
            m_changed |= variableAccessData->tallyVotesForShouldUseDoubleFormat();
        }
        propagateThroughArgumentPositions();
        for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
            VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
            if (!variableAccessData->isRoot())
                continue;
            m_changed |= variableAccessData->makePredictionForDoubleFormat();
        }
    }

    void propagateThroughArgumentPositions()
    {
        for (unsigned i = 0; i < m_graph.m_argumentPositions.size(); ++i)
            m_changed |= m_graph.m_argumentPositions[i].mergeArgumentPredictionAwareness();
    }

    // Sets any predictions that do not depends on other nodes.
    void processInvariants()
    {
        for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
            for (Node* node : *block) {
                m_currentNode = node;
                processInvariantsForNode();
            }
        }
    }

    void processInvariantsForNode()
    {
        switch (m_currentNode->op()) {
        case JSConstant: {
            SpeculatedType type = speculationFromValue(m_currentNode->asJSValue());
            if (type == SpecAnyIntAsDouble && enableInt52())
                type = int52AwareSpeculationFromValue(m_currentNode->asJSValue());
            setPrediction(type);
            break;
        }
        case DoubleConstant: {
            SpeculatedType type = speculationFromValue(m_currentNode->asJSValue());
            setPrediction(type);
            break;
        }

        case ArithBitNot:
        case ArithBitAnd:
        case ArithBitOr:
        case ArithBitXor:
        case BitRShift:
        case ArithBitLShift:
        case BitURShift:
        case ArithIMul:
        case ArithClz32: {
            setPrediction(SpecInt32Only);
            break;
        }

        case ArrayPop:
        case ArrayPush:
        case RegExpExec:
        case RegExpExecNonGlobalOrSticky:
        case RegExpTest:
        case RegExpMatchFast:
        case RegExpMatchFastGlobal:
        case StringReplace:
        case StringReplaceRegExp:
        case GetById:
        case GetByIdFlush:
        case GetByIdWithThis:
        case GetByIdDirect:
        case GetByIdDirectFlush:
        case TryGetById:
        case GetByValWithThis:
        case GetByOffset:
        case MultiGetByOffset:
        case GetDirectPname:
        case Call:
        case DirectCall:
        case TailCallInlinedCaller:
        case DirectTailCallInlinedCaller:
        case Construct:
        case DirectConstruct:
        case CallVarargs:
        case CallEval:
        case TailCallVarargsInlinedCaller:
        case ConstructVarargs:
        case CallForwardVarargs:
        case ConstructForwardVarargs:
        case TailCallForwardVarargsInlinedCaller:
        case GetGlobalVar:
        case GetGlobalLexicalVariable:
        case GetClosureVar:
        case GetFromArguments:
        case LoadKeyFromMapBucket:
        case LoadValueFromMapBucket:
        case ToNumber:
        case ToObject:
        case ValueBitAnd:
        case ValueBitXor:
        case ValueBitOr:
        case ValueBitNot:
        case CallObjectConstructor:
        case GetArgument:
        case CallDOMGetter:
        case GetDynamicVar:
        case GetPrototypeOf:
        case ExtractValueFromWeakMapGet:
        case DataViewGetInt:
        case DataViewGetFloat: {
            setPrediction(m_currentNode->getHeapPrediction());
            break;
        }

        case WeakMapGet:
        case ResolveScopeForHoistingFuncDeclInEval: {
            setPrediction(SpecBytecodeTop);
            break;
        }

        case GetGetterSetterByOffset:
        case GetExecutable: {
            setPrediction(SpecCellOther);
            break;
        }

        case GetGetter:
        case GetSetter:
        case GetCallee:
        case NewFunction:
        case NewGeneratorFunction:
        case NewAsyncGeneratorFunction:
        case NewAsyncFunction: {
            setPrediction(SpecFunction);
            break;
        }

        case GetArgumentCountIncludingThis: {
            setPrediction(SpecInt32Only);
            break;
        }

        case SetCallee:
        case SetArgumentCountIncludingThis:
            break;

        case MapHash:
            setPrediction(SpecInt32Only);
            break;

        case GetMapBucket:
        case GetMapBucketHead:
        case GetMapBucketNext:
        case SetAdd:
        case MapSet:
            setPrediction(SpecCellOther);
            break;

        case GetRestLength:
        case ArrayIndexOf: {
            setPrediction(SpecInt32Only);
            break;
        }

        case GetTypedArrayByteOffset:
        case GetArrayLength:
        case GetVectorLength: {
            setPrediction(SpecInt32Only);
            break;
        }

        case StringCharCodeAt: {
            setPrediction(SpecInt32Only);
            break;
        }

        case StringValueOf:
        case StringSlice:
        case ToLowerCase:
            setPrediction(SpecString);
            break;

        case ArithPow:
        case ArithSqrt:
        case ArithFRound:
        case ArithUnary: {
            setPrediction(SpecBytecodeDouble);
            break;
        }

        case ArithRound:
        case ArithFloor:
        case ArithCeil:
        case ArithTrunc: {
            if (isInt32OrBooleanSpeculation(m_currentNode->getHeapPrediction())
                && m_graph.roundShouldSpeculateInt32(m_currentNode, m_pass))
                setPrediction(SpecInt32Only);
            else
                setPrediction(SpecBytecodeDouble);
            break;
        }

        case ArithRandom: {
            setPrediction(SpecDoubleReal);
            break;
        }
        case DeleteByVal:
        case DeleteById:
        case LogicalNot:
        case CompareLess:
        case CompareLessEq:
        case CompareGreater:
        case CompareGreaterEq:
        case CompareBelow:
        case CompareBelowEq:
        case CompareEq:
        case CompareStrictEq:
        case CompareEqPtr:
        case SameValue:
        case OverridesHasInstance:
        case InstanceOf:
        case InstanceOfCustom:
        case IsEmpty:
        case IsUndefined:
        case IsUndefinedOrNull:
        case IsBoolean:
        case IsNumber:
        case NumberIsInteger:
        case IsObject:
        case IsObjectOrNull:
        case IsFunction:
        case IsCellWithType:
        case IsTypedArrayView:
        case MatchStructure: {
            setPrediction(SpecBoolean);
            break;
        }

        case TypeOf: {
            setPrediction(SpecStringIdent);
            break;
        }
        case GetButterfly:
        case GetIndexedPropertyStorage:
        case AllocatePropertyStorage:
        case ReallocatePropertyStorage: {
            setPrediction(SpecOther);
            break;
        }

        case CheckSubClass:
            break;

        case SkipScope:
        case GetGlobalObject: {
            setPrediction(SpecObjectOther);
            break;
        }

        case GetGlobalThis:
            setPrediction(SpecObject);
            break;

        case ResolveScope: {
            setPrediction(SpecObjectOther);
            break;
        }

        case ObjectCreate:
        case CreateThis:
        case NewObject: {
            setPrediction(SpecFinalObject);
            break;
        }

        case ArraySlice:
        case NewArrayWithSpread:
        case NewArray:
        case NewArrayWithSize:
        case CreateRest:
        case NewArrayBuffer:
        case ObjectKeys: {
            setPrediction(SpecArray);
            break;
        }

        case Spread:
            setPrediction(SpecCellOther);
            break;

        case NewTypedArray: {
            setPrediction(speculationFromTypedArrayType(m_currentNode->typedArrayType()));
            break;
        }

        case NewRegexp: {
            setPrediction(SpecRegExpObject);
            break;
        }

        case PushWithScope:
        case CreateActivation: {
            setPrediction(SpecObjectOther);
            break;
        }

        case StringFromCharCode: {
            setPrediction(SpecString);
            m_currentNode->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsInt);
            break;
        }
        case StringCharAt:
        case CallStringConstructor:
        case ToString:
        case NumberToStringWithRadix:
        case NumberToStringWithValidRadixConstant:
        case MakeRope:
        case StrCat: {
            setPrediction(SpecString);
            break;
        }
        case NewStringObject: {
            setPrediction(SpecStringObject);
            break;
        }
        case NewSymbol: {
            setPrediction(SpecSymbol);
            break;
        }

        case CreateDirectArguments: {
            setPrediction(SpecDirectArguments);
            break;
        }

        case CreateScopedArguments: {
            setPrediction(SpecScopedArguments);
            break;
        }

        case CreateClonedArguments: {
            setPrediction(SpecObjectOther);
            break;
        }

        case FiatInt52: {
            RELEASE_ASSERT(enableInt52());
            setPrediction(SpecInt52Any);
            break;
        }

        case GetScope:
            setPrediction(SpecObjectOther);
            break;

        case InByVal:
        case InById:
            setPrediction(SpecBoolean);
            break;

        case HasOwnProperty:
            setPrediction(SpecBoolean);
            break;

        case GetEnumerableLength: {
            setPrediction(SpecInt32Only);
            break;
        }
        case HasGenericProperty:
        case HasStructureProperty:
        case HasIndexedProperty: {
            setPrediction(SpecBoolean);
            break;
        }
        case GetPropertyEnumerator: {
            setPrediction(SpecCell);
            break;
        }
        case GetEnumeratorStructurePname: {
            setPrediction(SpecCell | SpecOther);
            break;
        }
        case GetEnumeratorGenericPname: {
            setPrediction(SpecCell | SpecOther);
            break;
        }
        case ToIndexString: {
            setPrediction(SpecString);
            break;
        }
        case ParseInt: {
            // We expect this node to almost always produce an int32. However,
            // it's possible it produces NaN or integers out of int32 range. We
            // rely on the heap prediction since the parseInt() call profiled
            // its result.
            setPrediction(m_currentNode->getHeapPrediction());
            break;
        }

        case IdentityWithProfile: {
            setPrediction(m_currentNode->getForcedPrediction());
            break;
        }

        case ExtractCatchLocal: {
            setPrediction(m_currentNode->catchLocalPrediction());
            break;
        }

        case GetLocal:
        case SetLocal:
        case UInt32ToNumber:
        case ValueNegate:
        case ValueAdd:
        case ValueSub:
        case ValueMul:
        case ValueDiv:
        case ValueMod:
        case ValuePow:
        case ValueBitLShift:
        case ArithAdd:
        case ArithSub:
        case ArithNegate:
        case ArithMin:
        case ArithMax:
        case ArithMul:
        case ArithDiv:
        case ArithMod:
        case ArithAbs:
        case GetByVal:
        case ToThis:
        case ToPrimitive:
        case NormalizeMapKey:
        case AtomicsAdd:
        case AtomicsAnd:
        case AtomicsCompareExchange:
        case AtomicsExchange:
        case AtomicsLoad:
        case AtomicsOr:
        case AtomicsStore:
        case AtomicsSub:
        case AtomicsXor: {
            m_dependentNodes.append(m_currentNode);
            break;
        }

        case AtomicsIsLockFree: {
            setPrediction(SpecBoolean);
            break;
        }

        case CPUIntrinsic: {
            if (m_currentNode->intrinsic() == CPURdtscIntrinsic)
                setPrediction(SpecInt32Only);
            else
                setPrediction(SpecOther);
            break;
        }

        case PutByValAlias:
        case DoubleAsInt32:
        case CheckArray:
        case CheckTypeInfoFlags:
        case Arrayify:
        case ArrayifyToStructure:
        case CheckTierUpInLoop:
        case CheckTierUpAtReturn:
        case CheckTierUpAndOSREnter:
        case CheckInBounds:
        case ValueToInt32:
        case DoubleRep:
        case ValueRep:
        case Int52Rep:
        case Int52Constant:
        case Identity:
        case BooleanToNumber:
        case PhantomNewObject:
        case PhantomNewFunction:
        case PhantomNewGeneratorFunction:
        case PhantomNewAsyncGeneratorFunction:
        case PhantomNewAsyncFunction:
        case PhantomCreateActivation:
        case PhantomDirectArguments:
        case PhantomCreateRest:
        case PhantomSpread:
        case PhantomNewArrayWithSpread:
        case PhantomNewArrayBuffer:
        case PhantomClonedArguments:
        case PhantomNewRegexp:
        case GetMyArgumentByVal:
        case GetMyArgumentByValOutOfBounds:
        case PutHint:
        case CheckStructureImmediate:
        case CheckStructureOrEmpty:
        case MaterializeNewObject:
        case MaterializeCreateActivation:
        case PutStack:
        case KillStack:
        case StoreBarrier:
        case FencedStoreBarrier:
        case GetStack:
        case GetRegExpObjectLastIndex:
        case SetRegExpObjectLastIndex:
        case RecordRegExpCachedResult:
        case LazyJSConstant:
        case CallDOM: {
            // This node should never be visible at this stage of compilation.
            DFG_CRASH(m_graph, m_currentNode, "Unexpected node during prediction propagation");
            break;
        }

        case Phi:
            // Phis should not be visible here since we're iterating the all-but-Phi's
            // part of basic blocks.
            RELEASE_ASSERT_NOT_REACHED();
            break;

        case EntrySwitch:
        case Upsilon:
            // These don't get inserted until we go into SSA.
            RELEASE_ASSERT_NOT_REACHED();
            break;

#ifndef NDEBUG
        // These get ignored because they don't return anything.
        case PutByValDirect:
        case PutByValWithThis:
        case PutByIdWithThis:
        case PutByVal:
        case PutClosureVar:
        case PutToArguments:
        case Return:
        case Throw:
        case ThrowStaticError:
        case TailCall:
        case DirectTailCall:
        case TailCallVarargs:
        case TailCallForwardVarargs:
        case PutById:
        case PutByIdFlush:
        case PutByIdDirect:
        case PutByOffset:
        case MultiPutByOffset:
        case PutGetterById:
        case PutSetterById:
        case PutGetterSetterById:
        case PutGetterByVal:
        case PutSetterByVal:
        case DefineDataProperty:
        case DefineAccessorProperty:
        case DFG::Jump:
        case Branch:
        case Switch:
        case ProfileType:
        case ProfileControlFlow:
        case ForceOSRExit:
        case SetArgumentDefinitely:
        case SetArgumentMaybe:
        case SetFunctionName:
        case CheckStructure:
        case CheckCell:
        case CheckNotEmpty:
        case AssertNotEmpty:
        case CheckStringIdent:
        case CheckBadCell:
        case PutStructure:
        case Phantom:
        case Check:
        case CheckVarargs:
        case PutGlobalVariable:
        case CheckTraps:
        case LogShadowChickenPrologue:
        case LogShadowChickenTail:
        case Unreachable:
        case LoopHint:
        case NotifyWrite:
        case ConstantStoragePointer:
        case MovHint:
        case ZombieHint:
        case ExitOK:
        case LoadVarargs:
        case ForwardVarargs:
        case PutDynamicVar:
        case NukeStructureAndSetButterfly:
        case InitializeEntrypointArguments:
        case WeakSetAdd:
        case WeakMapSet:
        case FilterCallLinkStatus:
        case FilterGetByIdStatus:
        case FilterPutByIdStatus:
        case FilterInByIdStatus:
        case ClearCatchLocals:
        case DataViewSet:
        case InvalidationPoint:
            break;

        // This gets ignored because it only pretends to produce a value.
        case BottomValue:
            break;

        // This gets ignored because it already has a prediction.
        case ExtractOSREntryLocal:
            break;

        // These gets ignored because it doesn't do anything.
        case CountExecution:
        case SuperSamplerBegin:
        case SuperSamplerEnd:
        case PhantomLocal:
        case Flush:
            break;

        case LastNodeType:
            RELEASE_ASSERT_NOT_REACHED();
            break;
#else
        default:
            break;
#endif
        }
    }

    SpeculatedType resultOfToPrimitive(SpeculatedType type)
    {
        if (type & SpecObject) {
            // We try to be optimistic here about StringObjects since it's unlikely that
            // someone overrides the valueOf or toString methods.
            if (type & SpecStringObject && m_graph.canOptimizeStringObjectAccess(m_currentNode->origin.semantic))
                return mergeSpeculations(type & ~SpecObject, SpecString);

            return mergeSpeculations(type & ~SpecObject, SpecPrimitive);
        }

        return type;
    }

    Vector<Node*> m_dependentNodes;
    Node* m_currentNode;
    bool m_changed { false };
    PredictionPass m_pass { PrimaryPass }; // We use different logic for considering predictions depending on how far along we are in propagation.
};

} // Anonymous namespace.

bool performPredictionPropagation(Graph& graph)
{
    return runPhase<PredictionPropagationPhase>(graph);
}

} } // namespace JSC::DFG

#endif // ENABLE(DFG_JIT)

