| /* |
| * 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 ValueBitRShift: |
| 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 ArithBitRShift: |
| 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 GetInternalField: |
| 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: |
| case StringCodePointAt: { |
| 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 CreatePromise: |
| case NewPromise: |
| setPrediction(SpecPromiseObject); |
| break; |
| |
| case CreateGenerator: |
| case NewGenerator: |
| case CreateAsyncGenerator: |
| case NewAsyncGenerator: |
| setPrediction(SpecObjectOther); |
| 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 ValueBitRShift: |
| 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 PutInternalField: |
| 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) |
| |