|  | /* | 
|  | * Copyright (C) 2011-2016 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); | 
|  |  | 
|  | propagateThroughArgumentPositions(); | 
|  |  | 
|  | processInvariants(); | 
|  |  | 
|  | m_pass = PrimaryPass; | 
|  | 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 & SpecInt52Only)) | 
|  | prediction = (prediction | SpecAnyIntAsDouble) & ~SpecInt52Only; | 
|  | 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(SpecAnyInt); | 
|  | else | 
|  | changed |= mergePrediction(SpecBytecodeNumber); | 
|  | 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.addShouldSpeculateAnyInt(node)) | 
|  | changed |= mergePrediction(SpecInt52Only); | 
|  | 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 { | 
|  | changed |= mergePrediction(SpecInt32Only); | 
|  | if (node->mayHaveDoubleResult()) | 
|  | changed |= mergePrediction(SpecBytecodeDouble); | 
|  | if (node->mayHaveNonNumberResult()) | 
|  | 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.addShouldSpeculateAnyInt(node)) | 
|  | changed |= mergePrediction(SpecInt52Only); | 
|  | 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.addShouldSpeculateAnyInt(node)) | 
|  | changed |= mergePrediction(SpecInt52Only); | 
|  | else | 
|  | changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right)); | 
|  | } else if (node->mayHaveNonIntResult() || (left & SpecBytecodeDouble) || (right & SpecBytecodeDouble)) | 
|  | changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble); | 
|  | else | 
|  | changed |= mergePrediction(SpecInt32Only); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case ArithNegate: | 
|  | if (node->child1()->prediction()) { | 
|  | if (m_graph.unaryArithShouldSpeculateInt32(node, m_pass)) | 
|  | changed |= mergePrediction(SpecInt32Only); | 
|  | else if (m_graph.unaryArithShouldSpeculateAnyInt(node, m_pass)) | 
|  | changed |= mergePrediction(SpecInt52Only); | 
|  | else | 
|  | changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction())); | 
|  | } | 
|  | 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 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.binaryArithShouldSpeculateAnyInt(node, m_pass)) | 
|  | changed |= mergePrediction(SpecInt52Only); | 
|  | else | 
|  | changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right)); | 
|  | } else { | 
|  | if (node->mayHaveNonIntResult() | 
|  | || (left & SpecBytecodeDouble) | 
|  | || (right & SpecBytecodeDouble)) | 
|  | changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble); | 
|  | else | 
|  | changed |= mergePrediction(SpecInt32Only); | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | 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 | 
|  | changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case ArithRound: | 
|  | case ArithFloor: | 
|  | case ArithCeil: | 
|  | case ArithTrunc: { | 
|  | if (isInt32OrBooleanSpeculation(node->getHeapPrediction()) && m_graph.roundShouldSpeculateInt32(node, m_pass)) | 
|  | changed |= setPrediction(SpecInt32Only); | 
|  | else | 
|  | changed |= setPrediction(SpecBytecodeDouble); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case ArithAbs: { | 
|  | SpeculatedType child = node->child1()->prediction(); | 
|  | if (isInt32OrBooleanSpeculationForArithmetic(child) | 
|  | && node->canSpeculateInt32(m_pass)) | 
|  | changed |= mergePrediction(SpecInt32Only); | 
|  | else | 
|  | changed |= mergePrediction(speculatedDoubleTypeForPrediction(child)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case GetByVal: { | 
|  | if (!node->child1()->prediction()) | 
|  | break; | 
|  |  | 
|  | ArrayMode arrayMode = node->arrayMode().refine( | 
|  | m_graph, node, | 
|  | node->child1()->prediction(), | 
|  | node->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 | 
|  | changed |= mergePrediction(SpecDoubleReal); | 
|  | break; | 
|  | case Array::Float32Array: | 
|  | case Array::Float64Array: | 
|  | changed |= mergePrediction(SpecFullDouble); | 
|  | break; | 
|  | case Array::Uint32Array: | 
|  | if (isInt32SpeculationForArithmetic(node->getHeapPrediction())) | 
|  | changed |= mergePrediction(SpecInt32Only); | 
|  | else if (enableInt52()) | 
|  | changed |= mergePrediction(SpecAnyInt); | 
|  | 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. | 
|  | ECMAMode ecmaMode = m_graph.executableFor(node->origin.semantic)->isStrictMode() ? StrictMode : NotStrictMode; | 
|  | if (ecmaMode == StrictMode) { | 
|  | if (node->child1()->shouldSpeculateBoolean()) { | 
|  | changed |= mergePrediction(SpecBoolean); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (node->child1()->shouldSpeculateInt32()) { | 
|  | changed |= mergePrediction(SpecInt32Only); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (enableInt52() && node->child1()->shouldSpeculateAnyInt()) { | 
|  | changed |= mergePrediction(SpecAnyInt); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (node->child1()->shouldSpeculateNumber()) { | 
|  | changed |= mergePrediction(SpecAnyInt); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (node->child1()->shouldSpeculateSymbol()) { | 
|  | changed |= mergePrediction(SpecSymbol); | 
|  | 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 (prediction) { | 
|  | if (prediction & ~SpecObject) { | 
|  | // Wrapper objects are created only in sloppy mode. | 
|  | if (ecmaMode != StrictMode) { | 
|  | prediction &= SpecObject; | 
|  | prediction = mergeSpeculations(prediction, SpecObjectOther); | 
|  | } | 
|  | } | 
|  | changed |= mergePrediction(prediction); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case ToPrimitive: { | 
|  | SpeculatedType child = node->child1()->prediction(); | 
|  | if (child) | 
|  | changed |= mergePrediction(resultOfToPrimitive(child)); | 
|  | 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 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.addShouldSpeculateAnyInt(node)) | 
|  | ballot = VoteDouble; | 
|  | else | 
|  | ballot = VoteValue; | 
|  |  | 
|  | m_graph.voteNode(node->child1(), ballot, weight); | 
|  | m_graph.voteNode(node->child2(), ballot, weight); | 
|  | break; | 
|  | } | 
|  |  | 
|  | 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.binaryArithShouldSpeculateAnyInt(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 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 ArithCos: | 
|  | case ArithSin: | 
|  | case ArithLog: | 
|  | 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) | 
|  | || isInt32Speculation(prediction) || isAnyIntSpeculation(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 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 = SpecInt52Only; | 
|  | setPrediction(type); | 
|  | break; | 
|  | } | 
|  | case DoubleConstant: { | 
|  | SpeculatedType type = speculationFromValue(m_currentNode->asJSValue()); | 
|  | setPrediction(type); | 
|  | break; | 
|  | } | 
|  | case BitAnd: | 
|  | case BitOr: | 
|  | case BitXor: | 
|  | case BitRShift: | 
|  | case BitLShift: | 
|  | case BitURShift: | 
|  | case ArithIMul: | 
|  | case ArithClz32: { | 
|  | setPrediction(SpecInt32Only); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case GetByValWithThis: | 
|  | case GetByIdWithThis: { | 
|  | setPrediction(SpecBytecodeTop); | 
|  | break; | 
|  | } | 
|  | case TryGetById: { | 
|  | setPrediction(SpecBytecodeTop); | 
|  | break; | 
|  | } | 
|  | case ArrayPop: | 
|  | case ArrayPush: | 
|  | case RegExpExec: | 
|  | case RegExpTest: | 
|  | case StringReplace: | 
|  | case StringReplaceRegExp: | 
|  | case GetById: | 
|  | case GetByIdFlush: | 
|  | case GetByOffset: | 
|  | case MultiGetByOffset: | 
|  | case GetDirectPname: | 
|  | case Call: | 
|  | case TailCallInlinedCaller: | 
|  | case Construct: | 
|  | case CallVarargs: | 
|  | case TailCallVarargsInlinedCaller: | 
|  | case ConstructVarargs: | 
|  | case CallForwardVarargs: | 
|  | case ConstructForwardVarargs: | 
|  | case TailCallForwardVarargsInlinedCaller: | 
|  | case GetGlobalVar: | 
|  | case GetGlobalLexicalVariable: | 
|  | case GetClosureVar: | 
|  | case GetFromArguments: | 
|  | case ToNumber: { | 
|  | setPrediction(m_currentNode->getHeapPrediction()); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case GetDynamicVar: { | 
|  | setPrediction(SpecBytecodeTop); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case GetGetterSetterByOffset: | 
|  | case GetExecutable: { | 
|  | setPrediction(SpecCellOther); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case GetGetter: | 
|  | case GetSetter: | 
|  | case GetCallee: | 
|  | case NewFunction: | 
|  | case NewGeneratorFunction: { | 
|  | setPrediction(SpecFunction); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case GetArgumentCountIncludingThis: { | 
|  | setPrediction(SpecInt32Only); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case GetRestLength: { | 
|  | setPrediction(SpecInt32Only); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case GetTypedArrayByteOffset: | 
|  | case GetArrayLength: { | 
|  | setPrediction(SpecInt32Only); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case StringCharCodeAt: { | 
|  | setPrediction(SpecInt32Only); | 
|  | break; | 
|  | } | 
|  | case ArithPow: | 
|  | case ArithSqrt: | 
|  | case ArithFRound: | 
|  | case ArithSin: | 
|  | case ArithCos: | 
|  | case ArithLog: { | 
|  | setPrediction(SpecBytecodeDouble); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case ArithRandom: { | 
|  | setPrediction(SpecDoubleReal); | 
|  | break; | 
|  | } | 
|  | case DeleteByVal: | 
|  | case DeleteById: | 
|  | case LogicalNot: | 
|  | case CompareLess: | 
|  | case CompareLessEq: | 
|  | case CompareGreater: | 
|  | case CompareGreaterEq: | 
|  | case CompareEq: | 
|  | case CompareStrictEq: | 
|  | case OverridesHasInstance: | 
|  | case InstanceOf: | 
|  | case InstanceOfCustom: | 
|  | case IsJSArray: | 
|  | case IsEmpty: | 
|  | case IsUndefined: | 
|  | case IsBoolean: | 
|  | case IsNumber: | 
|  | case IsString: | 
|  | case IsObject: | 
|  | case IsObjectOrNull: | 
|  | case IsFunction: | 
|  | case IsRegExpObject: | 
|  | case IsTypedArrayView: { | 
|  | setPrediction(SpecBoolean); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case TypeOf: { | 
|  | setPrediction(SpecStringIdent); | 
|  | break; | 
|  | } | 
|  | case GetButterfly: | 
|  | case GetIndexedPropertyStorage: | 
|  | case AllocatePropertyStorage: | 
|  | case ReallocatePropertyStorage: { | 
|  | setPrediction(SpecOther); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case CallObjectConstructor: { | 
|  | setPrediction(SpecObject); | 
|  | break; | 
|  | } | 
|  | case SkipScope: | 
|  | case GetGlobalObject: { | 
|  | setPrediction(SpecObjectOther); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case ResolveScope: { | 
|  | setPrediction(SpecObjectOther); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case CreateThis: | 
|  | case NewObject: { | 
|  | setPrediction(SpecFinalObject); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case NewArray: | 
|  | case NewArrayWithSize: | 
|  | case NewArrayBuffer: { | 
|  | setPrediction(SpecArray); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case NewTypedArray: { | 
|  | setPrediction(speculationFromTypedArrayType(m_currentNode->typedArrayType())); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case NewRegexp: { | 
|  | setPrediction(SpecRegExpObject); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case CreateActivation: { | 
|  | setPrediction(SpecObjectOther); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case StringFromCharCode: { | 
|  | setPrediction(SpecString); | 
|  | m_currentNode->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsInt); | 
|  | break; | 
|  | } | 
|  | case StringCharAt: | 
|  | case CallStringConstructor: | 
|  | case ToString: | 
|  | case MakeRope: | 
|  | case StrCat: { | 
|  | setPrediction(SpecString); | 
|  | break; | 
|  | } | 
|  | case NewStringObject: { | 
|  | setPrediction(SpecStringObject); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case CreateDirectArguments: { | 
|  | setPrediction(SpecDirectArguments); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case CreateScopedArguments: { | 
|  | setPrediction(SpecScopedArguments); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case CreateClonedArguments: { | 
|  | setPrediction(SpecObjectOther); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case FiatInt52: { | 
|  | RELEASE_ASSERT(enableInt52()); | 
|  | setPrediction(SpecAnyInt); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case GetScope: | 
|  | setPrediction(SpecObjectOther); | 
|  | break; | 
|  |  | 
|  | case In: | 
|  | 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 GetLocal: | 
|  | case SetLocal: | 
|  | case UInt32ToNumber: | 
|  | case ValueAdd: | 
|  | case ArithAdd: | 
|  | case ArithSub: | 
|  | case ArithNegate: | 
|  | case ArithMin: | 
|  | case ArithMax: | 
|  | case ArithMul: | 
|  | case ArithDiv: | 
|  | case ArithMod: | 
|  | case ArithRound: | 
|  | case ArithFloor: | 
|  | case ArithCeil: | 
|  | case ArithTrunc: | 
|  | case ArithAbs: | 
|  | case GetByVal: | 
|  | case ToThis: | 
|  | case ToPrimitive: { | 
|  | m_dependentNodes.append(m_currentNode); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case PutByValAlias: | 
|  | case DoubleAsInt32: | 
|  | case GetLocalUnlinked: | 
|  | case CheckArray: | 
|  | case CheckTypeInfoFlags: | 
|  | case Arrayify: | 
|  | case ArrayifyToStructure: | 
|  | case CheckTierUpInLoop: | 
|  | case CheckTierUpAtReturn: | 
|  | case CheckTierUpAndOSREnter: | 
|  | case InvalidationPoint: | 
|  | case CheckInBounds: | 
|  | case ValueToInt32: | 
|  | case DoubleRep: | 
|  | case ValueRep: | 
|  | case Int52Rep: | 
|  | case Int52Constant: | 
|  | case Identity: | 
|  | case BooleanToNumber: | 
|  | case PhantomNewObject: | 
|  | case PhantomNewFunction: | 
|  | case PhantomNewGeneratorFunction: | 
|  | case PhantomCreateActivation: | 
|  | case PhantomDirectArguments: | 
|  | case PhantomClonedArguments: | 
|  | case GetMyArgumentByVal: | 
|  | case GetMyArgumentByValOutOfBounds: | 
|  | case PutHint: | 
|  | case CheckStructureImmediate: | 
|  | case MaterializeNewObject: | 
|  | case MaterializeCreateActivation: | 
|  | case PutStack: | 
|  | case KillStack: | 
|  | case StoreBarrier: | 
|  | case GetStack: | 
|  | case GetRegExpObjectLastIndex: | 
|  | case SetRegExpObjectLastIndex: | 
|  | case RecordRegExpCachedResult: | 
|  | case LazyJSConstant: { | 
|  | // This node should never be visible at this stage of compilation. It is | 
|  | // inserted by fixup(), which follows this phase. | 
|  | 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 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 TailCall: | 
|  | case TailCallVarargs: | 
|  | case TailCallForwardVarargs: | 
|  | case Throw: | 
|  | case PutById: | 
|  | case PutByIdFlush: | 
|  | case PutByIdDirect: | 
|  | case PutByOffset: | 
|  | case MultiPutByOffset: | 
|  | case PutGetterById: | 
|  | case PutSetterById: | 
|  | case PutGetterSetterById: | 
|  | case PutGetterByVal: | 
|  | case PutSetterByVal: | 
|  | case DFG::Jump: | 
|  | case Branch: | 
|  | case Switch: | 
|  | case ProfileType: | 
|  | case ProfileControlFlow: | 
|  | case ThrowReferenceError: | 
|  | case ForceOSRExit: | 
|  | case SetArgument: | 
|  | case SetFunctionName: | 
|  | case CheckStructure: | 
|  | case CheckCell: | 
|  | case CheckNotEmpty: | 
|  | case CheckIdent: | 
|  | case CheckBadCell: | 
|  | case PutStructure: | 
|  | case VarInjectionWatchpoint: | 
|  | case Phantom: | 
|  | case Check: | 
|  | case PutGlobalVariable: | 
|  | case CheckWatchdogTimer: | 
|  | case LogShadowChickenPrologue: | 
|  | case LogShadowChickenTail: | 
|  | case Unreachable: | 
|  | case LoopHint: | 
|  | case NotifyWrite: | 
|  | case ConstantStoragePointer: | 
|  | case MovHint: | 
|  | case ZombieHint: | 
|  | case ExitOK: | 
|  | case LoadVarargs: | 
|  | case ForwardVarargs: | 
|  | case CopyRest: | 
|  | case PutDynamicVar: | 
|  | 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 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; | 
|  | PredictionPass m_pass; // 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) | 
|  |  |