From 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Tue, 27 Jun 2017 06:07:23 +0000 Subject: webkitgtk-2.16.5 --- .../dfg/DFGPredictionPropagationPhase.cpp | 1335 +++++++++++++------- 1 file changed, 857 insertions(+), 478 deletions(-) (limited to 'Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp') 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,398 +282,244 @@ 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)); - } - break; - } - - case ArithDiv: { - 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); - else - changed |= mergePrediction(SpecDouble); + // 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 (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) - && nodeCanSpeculateInt32(node->arithNodeFlags())) - changed |= mergePrediction(SpecInt32); - else - changed |= mergePrediction(SpecDouble); + 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 ArithSqrt: - case ArithSin: - case ArithCos: { - changed |= setPrediction(SpecDouble); - break; - } - + case ArithAbs: { - SpeculatedType child = node->child1()->prediction(); - if (isInt32SpeculationForArithmetic(child) - && nodeCanSpeculateInt32(node->arithNodeFlags())) - changed |= mergePrediction(SpecInt32); + SpeculatedType childPrediction = node->child1()->prediction(); + if (isInt32OrBooleanSpeculation(childPrediction) + && node->canSpeculateInt32(m_pass)) + changed |= mergePrediction(SpecInt32Only); else - changed |= mergePrediction(speculatedDoubleTypeForPrediction(child)); - break; - } - - case LogicalNot: - case CompareLess: - case CompareLessEq: - case CompareGreater: - case CompareGreaterEq: - case CompareEq: - case CompareEqConstant: - case CompareStrictEq: - case CompareStrictEqConstant: - case InstanceOf: - case IsUndefined: - case IsBoolean: - case IsNumber: - case IsString: - case IsObject: - case IsFunction: { - changed |= setPrediction(SpecBoolean); - break; - } - - case TypeOf: { - changed |= setPrediction(SpecString); + changed |= mergePrediction(SpecBytecodeDouble); 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); + 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 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(SpecInt52); - } 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; - } - - case GetMyArgumentsLengthSafe: { - changed |= setPrediction(SpecInt32); - break; - } - - case GetClosureRegisters: - case GetButterfly: - case GetIndexedPropertyStorage: - case AllocatePropertyStorage: - case ReallocatePropertyStorage: { - changed |= setPrediction(SpecOther); + 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) { - prediction &= SpecObject; - prediction = mergeSpeculations(prediction, SpecObjectOther); + // Wrapper objects are created only in sloppy mode. + if (ecmaMode != StrictMode) { + prediction &= SpecObject; + prediction = mergeSpeculations(prediction, SpecObjectOther); + } } changed |= mergePrediction(prediction); } break; } - case GetMyScope: - case SkipTopScope: - case SkipScope: { - changed |= setPrediction(SpecObjectOther); - break; - } - - case GetCallee: { - changed |= setPrediction(SpecFunction); - break; - } - - case CreateThis: - case NewObject: { - changed |= setPrediction(SpecFinalObject); - break; - } - - case NewArray: - case NewArrayWithSize: - case NewArrayBuffer: { - changed |= setPrediction(SpecArray); - break; - } - - case NewTypedArray: { - changed |= setPrediction(speculationFromTypedArrayType(node->typedArrayType())); - break; - } - - case NewRegexp: - case CreateActivation: { - changed |= setPrediction(SpecObjectOther); - break; - } - - case StringFromCharCode: { - changed |= setPrediction(SpecString); - changed |= node->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsInt); - break; - } - case StringCharAt: - case ToString: - case MakeRope: { - changed |= setPrediction(SpecString); - break; - } - case ToPrimitive: { SpeculatedType child = node->child1()->prediction(); if (child) changed |= mergePrediction(resultOfToPrimitive(child)); break; } - - case NewStringObject: { - changed |= setPrediction(SpecStringObject); + + default: break; } - - case CreateArguments: { - changed |= setPrediction(SpecArguments); - break; + + m_changed |= changed; + } + + void propagateForward() + { + for (Node* node : m_dependentNodes) { + m_currentNode = node; + propagate(m_currentNode); } - - case NewFunction: { - SpeculatedType child = node->child1()->prediction(); - if (child & SpecEmpty) - changed |= mergePrediction((child & ~SpecEmpty) | SpecFunction); + } + + 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 - changed |= mergePrediction(child); + ballot = VoteValue; + + m_graph.voteNode(node->child1(), ballot, weight); + m_graph.voteNode(node->child2(), ballot, weight); break; } - - case NewFunctionNoCheck: - case NewFunctionExpression: { - changed |= setPrediction(SpecFunction); - break; - } - - case PutByValAlias: - case GetArrayLength: - case GetTypedArrayByteOffset: - case Int32ToDouble: - case DoubleAsInt32: - case GetLocalUnlinked: - case GetMyArgumentsLength: - case GetMyArgumentByVal: - case PhantomPutStructure: - case PhantomArguments: - case CheckArray: - case Arrayify: - case ArrayifyToStructure: - case CheckTierUpInLoop: - case CheckTierUpAtReturn: - case CheckTierUpAndOSREnter: - case InvalidationPoint: - case Int52ToValue: - case Int52ToDouble: - case CheckInBounds: - case ValueToInt32: { - // This node should never be visible at this stage of compilation. It is - // inserted by fixup(), which follows this phase. - RELEASE_ASSERT_NOT_REACHED(); - 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: - 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 PutByVal: - case PutClosureVar: - case Return: - case Throw: - case PutById: - case PutByIdDirect: - case PutByOffset: - case DFG::Jump: - case Branch: - case Switch: - case Breakpoint: - case ProfileWillCall: - case ProfileDidCall: - case CheckHasInstance: - case ThrowReferenceError: - case ForceOSRExit: - case SetArgument: - case CheckStructure: - case CheckExecutable: - case StructureTransitionWatchpoint: - case CheckFunction: - case PutStructure: - case TearOffActivation: - case TearOffArguments: - case CheckArgumentsNotCreated: - case VariableWatchpoint: - case VarInjectionWatchpoint: - case AllocationProfileWatchpoint: - case Phantom: - case Check: - case PutGlobalVar: - case CheckWatchdogTimer: - case Unreachable: - case LoopHint: - case NotifyWrite: - case FunctionReentryWatchpoint: - case TypedArrayWatchpoint: - case ConstantStoragePointer: - case MovHint: - case ZombieHint: - 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 - } - - 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) - { - 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); + + 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; } @@ -685,41 +532,47 @@ private: DoubleBallot ballot; - if (isFullNumberSpeculation(left) && isFullNumberSpeculation(right) - && !(Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) && node->canSpeculateInt32())) + if (isFullNumberSpeculation(left) + && isFullNumberSpeculation(right) + && !m_graph.binaryArithShouldSpeculateInt32(node, m_pass)) ballot = VoteDouble; else ballot = VoteValue; - m_graph.voteNode(node->child1(), ballot); - m_graph.voteNode(node->child2(), ballot); + m_graph.voteNode(node->child1(), ballot, weight); + m_graph.voteNode(node->child2(), ballot, weight); break; } - + case ArithAbs: DoubleBallot ballot; - if (!(node->child1()->shouldSpeculateInt32ForArithmetic() && node->canSpeculateInt32())) + if (node->child1()->shouldSpeculateNumber() + && !m_graph.unaryArithShouldSpeculateInt32(node, m_pass)) ballot = VoteDouble; else ballot = VoteValue; - m_graph.voteNode(node->child1(), ballot); + m_graph.voteNode(node->child1(), ballot, weight); break; case ArithSqrt: case ArithCos: case ArithSin: - m_graph.voteNode(node->child1(), VoteDouble); + 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); - else if ( - !isFullNumberSpeculation(prediction) - || isInt32Speculation(prediction) || isMachineIntSpeculation(prediction)) - node->variableAccessData()->vote(VoteValue); + node->variableAccessData()->vote(VoteDouble, weight); + else if (!isFullNumberSpeculation(prediction) + || isInt32Speculation(prediction) || isAnyIntSpeculation(prediction)) + node->variableAccessData()->vote(VoteValue, weight); break; } @@ -729,14 +582,14 @@ private: 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); + m_graph.voteNode(child1, VoteValue, weight); + m_graph.voteNode(child2, VoteValue, weight); switch (node->arrayMode().type()) { case Array::Double: - m_graph.voteNode(child3, VoteDouble); + m_graph.voteNode(child3, VoteDouble, weight); break; default: - m_graph.voteNode(child3, VoteValue); + m_graph.voteNode(child3, VoteValue, weight); break; } break; @@ -747,7 +600,7 @@ private: break; default: - m_graph.voteChildren(node, VoteValue); + m_graph.voteChildren(node, VoteValue, weight); break; } } @@ -763,7 +616,7 @@ private: ASSERT(block->isReachable); for (unsigned i = 0; i < block->size(); ++i) { m_currentNode = block->at(i); - doDoubleVoting(m_currentNode); + doDoubleVoting(m_currentNode, block->executionCount); } } for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) { @@ -772,8 +625,7 @@ private: continue; m_changed |= variableAccessData->tallyVotesForShouldUseDoubleFormat(); } - for (unsigned i = 0; i < m_graph.m_argumentPositions.size(); ++i) - m_changed |= m_graph.m_argumentPositions[i].mergeArgumentPredictionAwareness(); + propagateThroughArgumentPositions(); for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) { VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i]; if (!variableAccessData->isRoot()) @@ -782,13 +634,540 @@ private: } } + 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: + case ArithTan: + case ArithLog: { + 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 CompareEq: + case CompareStrictEq: + case CompareEqPtr: + case OverridesHasInstance: + case InstanceOf: + case InstanceOfCustom: + case IsEmpty: + case IsUndefined: + case IsBoolean: + case IsNumber: + case IsObject: + case IsObjectOrNull: + case IsFunction: + case IsCellWithType: + case IsTypedArrayView: { + setPrediction(SpecBoolean); + break; + } + + case TypeOf: { + setPrediction(SpecStringIdent); + break; + } + case GetButterfly: + case GetIndexedPropertyStorage: + case AllocatePropertyStorage: + case ReallocatePropertyStorage: { + setPrediction(SpecOther); + break; + } + + case CheckDOM: + 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 ArraySlice: + case NewArrayWithSpread: + case NewArray: + case NewArrayWithSize: + case CreateRest: + case NewArrayBuffer: { + setPrediction(SpecArray); + break; + } + + case Spread: + setPrediction(SpecCellOther); + 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 NumberToStringWithRadix: + 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 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 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 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. + 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 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 ProfileType: + case ProfileControlFlow: + case ThrowStaticError: + case ForceOSRExit: + case SetArgument: + case SetFunctionName: + case CheckStructure: + case CheckCell: + case CheckNotEmpty: + case CheckStringIdent: + case CheckBadCell: + case PutStructure: + 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 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. + 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 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(graph); } -- cgit v1.2.1