diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp | 1253 |
1 files changed, 816 insertions, 437 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index d859849a3..d090a8677 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2012, 2013, 2014 Apple Inc. All rights reserved. + * 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 @@ -30,20 +30,13 @@ #include "DFGGraph.h" #include "DFGPhase.h" -#include "Operations.h" +#include "JSCInlines.h" namespace JSC { namespace DFG { -SpeculatedType resultOfToPrimitive(SpeculatedType type) -{ - if (type & SpecObject) { - // Objects get turned into strings. So if the input has hints of objectness, - // the output will have hinsts of stringiness. - return mergeSpeculations(type & ~SpecObject, SpecString); - } - - return type; -} +namespace { + +bool verboseFixPointLoops = false; class PredictionPropagationPhase : public Phase { public: @@ -56,12 +49,47 @@ public: { ASSERT(m_graph.m_form == ThreadedCPS); ASSERT(m_graph.m_unificationState == GloballyUnified); + + propagateThroughArgumentPositions(); + + processInvariants(); + + m_pass = PrimaryPass; + propagateToFixpoint(); - // 1) propagate predictions + 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(); @@ -75,22 +103,11 @@ public: m_changed = false; propagateBackward(); } while (m_changed); - - // 2) repropagate predictions while doing double voting. - do { - m_changed = false; - doRoundOfDoubleVoting(); - if (!m_changed) - break; - m_changed = false; - propagateForward(); - } while (m_changed); - - return true; + if (verboseFixPointLoops) + dataLog("Iterated ", counter, " times in propagateToFixpoint.\n"); } -private: bool setPrediction(SpeculatedType prediction) { ASSERT(m_currentNode->hasResult()); @@ -113,11 +130,14 @@ private: SpeculatedType speculatedDoubleTypeForPrediction(SpeculatedType value) { - if (!isFullNumberSpeculation(value)) - return SpecDouble; - if (value & SpecDoubleNaN) - return SpecDouble; - return SpecDoubleReal; + 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) @@ -132,20 +152,11 @@ private: bool changed = false; switch (op) { - case JSConstant: - case WeakJSConstant: { - SpeculatedType type = speculationFromValue(m_graph.valueOfJSConstant(node)); - if (type == SpecInt52AsDouble) - type = SpecInt52; - changed |= setPrediction(type); - break; - } - case GetLocal: { VariableAccessData* variable = node->variableAccessData(); SpeculatedType prediction = variable->prediction(); - if (variable->shouldNeverUnbox() && (prediction & SpecInt52)) - prediction = (prediction | SpecInt52AsDouble) & ~SpecInt52; + if (!variable->couldRepresentInt52() && (prediction & SpecInt52Only)) + prediction = (prediction | SpecAnyIntAsDouble) & ~SpecInt52Only; if (prediction) changed |= mergePrediction(prediction); break; @@ -156,44 +167,12 @@ private: changed |= variableAccessData->predict(node->child1()->prediction()); break; } - - case BitAnd: - case BitOr: - case BitXor: - case BitRShift: - case BitLShift: - case BitURShift: - case ArithIMul: { - changed |= setPrediction(SpecInt32); - break; - } - - case ArrayPop: - case ArrayPush: - case RegExpExec: - case RegExpTest: - case GetById: - case GetByIdFlush: - case GetMyArgumentByValSafe: - case GetByOffset: - case Call: - case Construct: - case GetGlobalVar: - case GetClosureVar: { - changed |= setPrediction(node->getHeapPrediction()); - break; - } - - case StringCharCodeAt: { - changed |= setPrediction(SpecInt32); - break; - } case UInt32ToNumber: { - // FIXME: Support Int52. - // https://bugs.webkit.org/show_bug.cgi?id=125704 - if (nodeCanSpeculateInt32(node->arithNodeFlags())) - changed |= mergePrediction(SpecInt32); + if (node->canSpeculateInt32(m_pass)) + changed |= mergePrediction(SpecInt32Only); + else if (enableInt52()) + changed |= mergePrediction(SpecAnyInt); else changed |= mergePrediction(SpecBytecodeNumber); break; @@ -204,33 +183,43 @@ private: SpeculatedType right = node->child2()->prediction(); if (left && right) { - if (isFullNumberSpeculationExpectingDefined(left) && isFullNumberSpeculationExpectingDefined(right)) { - if (m_graph.addSpeculationMode(node) != DontSpeculateInt32) - changed |= mergePrediction(SpecInt32); - else if (m_graph.addShouldSpeculateMachineInt(node)) - changed |= mergePrediction(SpecInt52); + 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 (!(left & SpecFullNumber) || !(right & SpecFullNumber)) { + } else if (isStringOrStringObjectSpeculation(left) && isStringOrStringObjectSpeculation(right)) { // left or right is definitely something other than a number. changed |= mergePrediction(SpecString); - } else - changed |= mergePrediction(SpecString | SpecInt32 | SpecDouble); + } 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) != DontSpeculateInt32) - changed |= mergePrediction(SpecInt32); - else if (m_graph.addShouldSpeculateMachineInt(node)) - changed |= mergePrediction(SpecInt52); - else + 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; } @@ -238,38 +227,50 @@ private: case ArithSub: { SpeculatedType left = node->child1()->prediction(); SpeculatedType right = node->child2()->prediction(); - + if (left && right) { - if (m_graph.addSpeculationMode(node) != DontSpeculateInt32) - changed |= mergePrediction(SpecInt32); - else if (m_graph.addShouldSpeculateMachineInt(node)) - changed |= mergePrediction(SpecInt52); + 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(speculatedDoubleTypeForPredictions(left, right)); + changed |= mergePrediction(SpecInt32Only); } break; } - - case ArithNegate: - if (node->child1()->prediction()) { - if (m_graph.negateShouldSpeculateInt32(node)) - changed |= mergePrediction(SpecInt32); - else if (m_graph.negateShouldSpeculateMachineInt(node)) - changed |= mergePrediction(SpecInt52); - else + + case ArithNegate: { + SpeculatedType prediction = node->child1()->prediction(); + if (prediction) { + if (isInt32OrBooleanSpeculation(prediction) && node->canSpeculateInt32(m_pass)) + changed |= mergePrediction(SpecInt32Only); + else if (m_graph.unaryArithShouldSpeculateAnyInt(node, m_pass)) + changed |= mergePrediction(SpecInt52Only); + else if (isBytecodeNumberSpeculation(prediction)) changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction())); + else { + changed |= mergePrediction(SpecInt32Only); + 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::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) - && nodeCanSpeculateInt32(node->arithNodeFlags())) - changed |= mergePrediction(SpecInt32); + if (Node::shouldSpeculateInt32OrBooleanForArithmetic(node->child1().node(), node->child2().node()) + && node->canSpeculateInt32(m_pass)) + changed |= mergePrediction(SpecInt32Only); else changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right)); } @@ -281,235 +282,770 @@ private: SpeculatedType right = node->child2()->prediction(); if (left && right) { - if (m_graph.mulShouldSpeculateInt32(node)) - changed |= mergePrediction(SpecInt32); - else if (m_graph.mulShouldSpeculateMachineInt(node)) - changed |= mergePrediction(SpecInt52); - else - changed |= mergePrediction(speculatedDoubleTypeForPredictions(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 ArithDiv: + case ArithMod: { SpeculatedType left = node->child1()->prediction(); SpeculatedType right = node->child2()->prediction(); if (left && right) { - if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) - && nodeCanSpeculateInt32(node->arithNodeFlags())) - changed |= mergePrediction(SpecInt32); + 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 ArithAbs: { + SpeculatedType childPrediction = node->child1()->prediction(); + if (isInt32OrBooleanSpeculation(childPrediction) + && node->canSpeculateInt32(m_pass)) + changed |= mergePrediction(SpecInt32Only); + else + changed |= mergePrediction(SpecBytecodeDouble); + 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(SpecDouble); + 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())) + 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(SpecBytecodeNumber); + 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 ArithMod: { + 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 ArithTan: + 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; + } - if (left && right) { - if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) - && nodeCanSpeculateInt32(node->arithNodeFlags())) - changed |= mergePrediction(SpecInt32); - else - changed |= mergePrediction(SpecDouble); + 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 ArrayPop: + case ArrayPush: + case RegExpExec: + case RegExpTest: + case StringReplace: + case StringReplaceRegExp: + case GetById: + case GetByIdFlush: + case GetByIdWithThis: + 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 LoadFromJSMapBucket: + case ToNumber: + case GetArgument: + case CallDOMGetter: { + 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: + case NewAsyncFunction: { + setPrediction(SpecFunction); + break; + } + + case GetArgumentCountIncludingThis: { + setPrediction(SpecInt32Only); + break; + } + + case MapHash: + setPrediction(SpecInt32Only); + break; + case GetMapBucket: + setPrediction(SpecCellOther); + break; + case IsNonEmptyMapBucket: + setPrediction(SpecBoolean); + break; + + case GetRestLength: { + setPrediction(SpecInt32Only); + break; + } + + case GetTypedArrayByteOffset: + case GetArrayLength: { + setPrediction(SpecInt32Only); + break; + } + + case StringCharCodeAt: { + setPrediction(SpecInt32Only); + break; + } + + case ToLowerCase: + setPrediction(SpecString); + break; + + case ArithPow: case ArithSqrt: + case ArithFRound: case ArithSin: - case ArithCos: { - changed |= setPrediction(SpecDouble); + case ArithCos: + case ArithTan: + case ArithLog: { + setPrediction(SpecBytecodeDouble); break; } - - case ArithAbs: { - SpeculatedType child = node->child1()->prediction(); - if (isInt32SpeculationForArithmetic(child) - && nodeCanSpeculateInt32(node->arithNodeFlags())) - changed |= mergePrediction(SpecInt32); + + case ArithRound: + case ArithFloor: + case ArithCeil: + case ArithTrunc: { + if (isInt32OrBooleanSpeculation(m_currentNode->getHeapPrediction()) + && m_graph.roundShouldSpeculateInt32(m_currentNode, m_pass)) + setPrediction(SpecInt32Only); else - changed |= mergePrediction(speculatedDoubleTypeForPrediction(child)); + 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 CompareEqConstant: case CompareStrictEq: - case CompareStrictEqConstant: + case CompareEqPtr: + case OverridesHasInstance: case InstanceOf: + case InstanceOfCustom: + case IsEmpty: case IsUndefined: case IsBoolean: case IsNumber: - case IsString: case IsObject: - case IsFunction: { - changed |= setPrediction(SpecBoolean); + case IsObjectOrNull: + case IsFunction: + case IsCellWithType: + case IsTypedArrayView: { + setPrediction(SpecBoolean); break; } case TypeOf: { - changed |= setPrediction(SpecString); + setPrediction(SpecStringIdent); break; } - - case GetByVal: { - if (!node->child1()->prediction()) - break; - if (!node->getHeapPrediction()) - break; - - if (node->child1()->shouldSpeculateFloat32Array() - || node->child1()->shouldSpeculateFloat64Array()) - changed |= mergePrediction(SpecDouble); - else if (node->child1()->shouldSpeculateUint32Array()) { - if (isInt32Speculation(node->getHeapPrediction())) - changed |= mergePrediction(SpecInt32); - else - changed |= mergePrediction(SpecInt52); - } else - changed |= mergePrediction(node->getHeapPrediction()); - break; - } - - case GetMyArgumentsLengthSafe: { - changed |= setPrediction(SpecInt32); - break; - } - - case GetClosureRegisters: - case GetButterfly: + case GetButterfly: case GetIndexedPropertyStorage: case AllocatePropertyStorage: case ReallocatePropertyStorage: { - changed |= setPrediction(SpecOther); + setPrediction(SpecOther); break; } - case ToThis: { - SpeculatedType prediction = node->child1()->prediction(); - if (prediction) { - if (prediction & ~SpecObject) { - prediction &= SpecObject; - prediction = mergeSpeculations(prediction, SpecObjectOther); - } - changed |= mergePrediction(prediction); - } + case CheckDOM: + break; + + case CallObjectConstructor: { + setPrediction(SpecObject); break; } - - case GetMyScope: - case SkipTopScope: - case SkipScope: { - changed |= setPrediction(SpecObjectOther); + case SkipScope: + case GetGlobalObject: { + setPrediction(SpecObjectOther); break; } - - case GetCallee: { - changed |= setPrediction(SpecFunction); + + case ResolveScope: { + setPrediction(SpecObjectOther); break; } case CreateThis: case NewObject: { - changed |= setPrediction(SpecFinalObject); + setPrediction(SpecFinalObject); break; } + case ArraySlice: + case NewArrayWithSpread: case NewArray: case NewArrayWithSize: + case CreateRest: case NewArrayBuffer: { - changed |= setPrediction(SpecArray); + setPrediction(SpecArray); break; } + + case Spread: + setPrediction(SpecCellOther); + break; case NewTypedArray: { - changed |= setPrediction(speculationFromTypedArrayType(node->typedArrayType())); + setPrediction(speculationFromTypedArrayType(m_currentNode->typedArrayType())); + break; + } + + case NewRegexp: { + setPrediction(SpecRegExpObject); break; } - case NewRegexp: case CreateActivation: { - changed |= setPrediction(SpecObjectOther); + setPrediction(SpecObjectOther); break; } case StringFromCharCode: { - changed |= setPrediction(SpecString); - changed |= node->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsInt); + setPrediction(SpecString); + m_currentNode->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsInt); break; } case StringCharAt: + case CallStringConstructor: case ToString: - case MakeRope: { - changed |= setPrediction(SpecString); + case NumberToStringWithRadix: + case MakeRope: + case StrCat: { + setPrediction(SpecString); break; } - - case ToPrimitive: { - SpeculatedType child = node->child1()->prediction(); - if (child) - changed |= mergePrediction(resultOfToPrimitive(child)); + case NewStringObject: { + setPrediction(SpecStringObject); break; } - case NewStringObject: { - changed |= setPrediction(SpecStringObject); + case CreateDirectArguments: { + setPrediction(SpecDirectArguments); break; } - case CreateArguments: { - changed |= setPrediction(SpecArguments); + case CreateScopedArguments: { + setPrediction(SpecScopedArguments); break; } - case NewFunction: { - SpeculatedType child = node->child1()->prediction(); - if (child & SpecEmpty) - changed |= mergePrediction((child & ~SpecEmpty) | SpecFunction); - else - changed |= mergePrediction(child); + case CreateClonedArguments: { + setPrediction(SpecObjectOther); break; } - case NewFunctionNoCheck: - case NewFunctionExpression: { - changed |= setPrediction(SpecFunction); + case FiatInt52: { + RELEASE_ASSERT(enableInt52()); + setPrediction(SpecAnyInt); break; } - + + case GetScope: + setPrediction(SpecObjectOther); + break; + + case In: + 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 GetLocal: + case SetLocal: + case UInt32ToNumber: + case ValueAdd: + case ArithAdd: + case ArithSub: + case ArithNegate: + case ArithMin: + case ArithMax: + case ArithMul: + case ArithDiv: + case ArithMod: + case ArithAbs: + case GetByVal: + case ToThis: + case ToPrimitive: { + m_dependentNodes.append(m_currentNode); + break; + } + case PutByValAlias: - case GetArrayLength: - case GetTypedArrayByteOffset: - case Int32ToDouble: case DoubleAsInt32: case GetLocalUnlinked: - case GetMyArgumentsLength: - case GetMyArgumentByVal: - case PhantomPutStructure: - case PhantomArguments: case CheckArray: + case CheckTypeInfoFlags: case Arrayify: case ArrayifyToStructure: case CheckTierUpInLoop: case CheckTierUpAtReturn: case CheckTierUpAndOSREnter: case InvalidationPoint: - case Int52ToValue: - case Int52ToDouble: case CheckInBounds: - case ValueToInt32: { + case ValueToInt32: + case DoubleRep: + case ValueRep: + case Int52Rep: + case Int52Constant: + case Identity: + case BooleanToNumber: + case PhantomNewObject: + case PhantomNewFunction: + case PhantomNewGeneratorFunction: + case PhantomNewAsyncFunction: + case PhantomCreateActivation: + case PhantomDirectArguments: + case PhantomCreateRest: + case PhantomSpread: + case PhantomNewArrayWithSpread: + case PhantomClonedArguments: + case GetMyArgumentByVal: + case GetMyArgumentByValOutOfBounds: + case PutHint: + case CheckStructureImmediate: + 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. It is // inserted by fixup(), which follows this phase. - RELEASE_ASSERT_NOT_REACHED(); + DFG_CRASH(m_graph, m_currentNode, "Unexpected node during prediction propagation"); break; } @@ -520,69 +1056,72 @@ private: break; case Upsilon: - case GetArgument: // These don't get inserted until we go into SSA. RELEASE_ASSERT_NOT_REACHED(); break; - case GetScope: - changed |= setPrediction(SpecObjectOther); - break; - - case In: - changed |= setPrediction(SpecBoolean); - break; - - case Identity: - changed |= mergePrediction(node->child1()->prediction()); - break; - #ifndef NDEBUG // These get ignored because they don't return anything. - case StoreBarrier: - case ConditionalStoreBarrier: - case StoreBarrierWithNullCheck: case PutByValDirect: + case PutByValWithThis: + case PutByIdWithThis: case PutByVal: case PutClosureVar: + case PutToArguments: case Return: + case TailCall: + case DirectTailCall: + 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 DefineDataProperty: + case DefineAccessorProperty: case DFG::Jump: case Branch: case Switch: - case Breakpoint: - case ProfileWillCall: - case ProfileDidCall: - case CheckHasInstance: - case ThrowReferenceError: + case ProfileType: + case ProfileControlFlow: + case ThrowStaticError: case ForceOSRExit: case SetArgument: + case SetFunctionName: case CheckStructure: - case CheckExecutable: - case StructureTransitionWatchpoint: - case CheckFunction: + case CheckCell: + case CheckNotEmpty: + case CheckStringIdent: + case CheckBadCell: case PutStructure: - case TearOffActivation: - case TearOffArguments: - case CheckArgumentsNotCreated: - case VariableWatchpoint: - case VarInjectionWatchpoint: - case AllocationProfileWatchpoint: case Phantom: case Check: - case PutGlobalVar: + case PutGlobalVariable: case CheckWatchdogTimer: + case LogShadowChickenPrologue: + case LogShadowChickenTail: case Unreachable: case LoopHint: case NotifyWrite: - case FunctionReentryWatchpoint: - case TypedArrayWatchpoint: case ConstantStoragePointer: case MovHint: case ZombieHint: + case ExitOK: + case LoadVarargs: + case ForwardVarargs: + case PutDynamicVar: + case NukeStructureAndSetButterfly: + break; + + // This gets ignored because it only pretends to produce a value. + case BottomValue: break; // This gets ignored because it already has a prediction. @@ -603,192 +1142,32 @@ private: break; #endif } - - m_changed |= changed; - } - - void propagateForward() - { - 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); - propagate(m_currentNode); - } - } } - - void propagateBackward() - { - for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { - BasicBlock* block = m_graph.block(blockIndex); - if (!block) - continue; - ASSERT(block->isReachable); - for (unsigned i = block->size(); i--;) { - m_currentNode = block->at(i); - propagate(m_currentNode); - } - } - } - - void doDoubleVoting(Node* node) + + SpeculatedType resultOfToPrimitive(SpeculatedType type) { - switch (node->op()) { - case ValueAdd: - case ArithAdd: - case ArithSub: { - SpeculatedType left = node->child1()->prediction(); - SpeculatedType right = node->child2()->prediction(); - - DoubleBallot ballot; - - if (isFullNumberSpeculationExpectingDefined(left) && isFullNumberSpeculationExpectingDefined(right) - && !m_graph.addShouldSpeculateInt32(node) - && !m_graph.addShouldSpeculateMachineInt(node)) - ballot = VoteDouble; - else - ballot = VoteValue; - - m_graph.voteNode(node->child1(), ballot); - m_graph.voteNode(node->child2(), ballot); - break; - } - - case ArithMul: { - SpeculatedType left = node->child1()->prediction(); - SpeculatedType right = node->child2()->prediction(); - - DoubleBallot ballot; - - if (isFullNumberSpeculation(left) && isFullNumberSpeculation(right) - && !m_graph.mulShouldSpeculateInt32(node) - && !m_graph.mulShouldSpeculateMachineInt(node)) - ballot = VoteDouble; - else - ballot = VoteValue; - - m_graph.voteNode(node->child1(), ballot); - m_graph.voteNode(node->child2(), ballot); - break; - } + 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); - 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) - && !(Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) && node->canSpeculateInt32())) - ballot = VoteDouble; - else - ballot = VoteValue; - - m_graph.voteNode(node->child1(), ballot); - m_graph.voteNode(node->child2(), ballot); - break; - } - - case ArithAbs: - DoubleBallot ballot; - if (!(node->child1()->shouldSpeculateInt32ForArithmetic() && node->canSpeculateInt32())) - ballot = VoteDouble; - else - ballot = VoteValue; - - m_graph.voteNode(node->child1(), ballot); - break; - - case ArithSqrt: - case ArithCos: - case ArithSin: - m_graph.voteNode(node->child1(), VoteDouble); - break; - - case SetLocal: { - SpeculatedType prediction = node->child1()->prediction(); - if (isDoubleSpeculation(prediction)) - node->variableAccessData()->vote(VoteDouble); - else if ( - !isFullNumberSpeculation(prediction) - || isInt32Speculation(prediction) || isMachineIntSpeculation(prediction)) - node->variableAccessData()->vote(VoteValue); - break; + return mergeSpeculations(type & ~SpecObject, SpecPrimitive); } - 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); - m_graph.voteNode(child2, VoteValue); - switch (node->arrayMode().type()) { - case Array::Double: - m_graph.voteNode(child3, VoteDouble); - break; - default: - m_graph.voteNode(child3, VoteValue); - break; - } - break; - } - - case MovHint: - // Ignore these since they have no effect on in-DFG execution. - break; - - default: - m_graph.voteChildren(node, VoteValue); - break; - } + return type; } - - 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); - } - } - 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(); - } - for (unsigned i = 0; i < m_graph.m_argumentPositions.size(); ++i) - m_changed |= m_graph.m_argumentPositions[i].mergeArgumentPredictionAwareness(); - 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(); - } - } - + + 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) { - SamplingRegion samplingRegion("DFG Prediction Propagation Phase"); return runPhase<PredictionPropagationPhase>(graph); } |