darling-JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
2020-08-29 15:28:47 -04:00

1390 lines
47 KiB
C++

/*
* Copyright (C) 2011-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "DFGPredictionPropagationPhase.h"
#if ENABLE(DFG_JIT)
#include "DFGGraph.h"
#include "DFGPhase.h"
#include "JSCInlines.h"
namespace JSC { namespace DFG {
namespace {
bool verboseFixPointLoops = false;
class PredictionPropagationPhase : public Phase {
public:
PredictionPropagationPhase(Graph& graph)
: Phase(graph, "prediction propagation")
{
}
bool run()
{
ASSERT(m_graph.m_form == ThreadedCPS);
ASSERT(m_graph.m_unificationState == GloballyUnified);
m_pass = PrimaryPass;
propagateThroughArgumentPositions();
processInvariants();
propagateToFixpoint();
m_pass = RareCasePass;
propagateToFixpoint();
m_pass = DoubleVotingPass;
unsigned counter = 0;
do {
if (verboseFixPointLoops)
++counter;
m_changed = false;
doRoundOfDoubleVoting();
if (!m_changed)
break;
m_changed = false;
propagateForward();
} while (m_changed);
if (verboseFixPointLoops)
dataLog("Iterated ", counter, " times in double voting fixpoint.\n");
return true;
}
private:
void propagateToFixpoint()
{
unsigned counter = 0;
do {
if (verboseFixPointLoops)
++counter;
m_changed = false;
// Forward propagation is near-optimal for both topologically-sorted and
// DFS-sorted code.
propagateForward();
if (!m_changed)
break;
// Backward propagation reduces the likelihood that pathological code will
// cause slowness. Loops (especially nested ones) resemble backward flow.
// This pass captures two cases: (1) it detects if the forward fixpoint
// found a sound solution and (2) short-circuits backward flow.
m_changed = false;
propagateBackward();
} while (m_changed);
if (verboseFixPointLoops)
dataLog("Iterated ", counter, " times in propagateToFixpoint.\n");
}
bool setPrediction(SpeculatedType prediction)
{
ASSERT(m_currentNode->hasResult());
// setPrediction() is used when we know that there is no way that we can change
// our minds about what the prediction is going to be. There is no semantic
// difference between setPrediction() and mergeSpeculation() other than the
// increased checking to validate this property.
ASSERT(m_currentNode->prediction() == SpecNone || m_currentNode->prediction() == prediction);
return m_currentNode->predict(prediction);
}
bool mergePrediction(SpeculatedType prediction)
{
ASSERT(m_currentNode->hasResult());
return m_currentNode->predict(prediction);
}
SpeculatedType speculatedDoubleTypeForPrediction(SpeculatedType value)
{
SpeculatedType result = SpecDoubleReal;
if (value & SpecDoubleImpureNaN)
result |= SpecDoubleImpureNaN;
if (value & SpecDoublePureNaN)
result |= SpecDoublePureNaN;
if (!isFullNumberOrBooleanSpeculation(value))
result |= SpecDoublePureNaN;
return result;
}
SpeculatedType speculatedDoubleTypeForPredictions(SpeculatedType left, SpeculatedType right)
{
return speculatedDoubleTypeForPrediction(mergeSpeculations(left, right));
}
void propagate(Node* node)
{
NodeType op = node->op();
bool changed = false;
switch (op) {
case GetLocal: {
VariableAccessData* variable = node->variableAccessData();
SpeculatedType prediction = variable->prediction();
if (!variable->couldRepresentInt52() && (prediction & SpecNonInt32AsInt52))
prediction = (prediction | SpecAnyIntAsDouble) & ~SpecNonInt32AsInt52;
if (prediction)
changed |= mergePrediction(prediction);
break;
}
case SetLocal: {
VariableAccessData* variableAccessData = node->variableAccessData();
changed |= variableAccessData->predict(node->child1()->prediction());
break;
}
case UInt32ToNumber: {
if (node->canSpeculateInt32(m_pass))
changed |= mergePrediction(SpecInt32Only);
else if (enableInt52())
changed |= mergePrediction(SpecInt52Any);
else
changed |= mergePrediction(SpecBytecodeNumber);
break;
}
case ValueBitLShift: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (isBigIntSpeculation(left) && isBigIntSpeculation(right))
changed |= mergePrediction(SpecBigInt);
else if (isFullNumberOrBooleanSpeculationExpectingDefined(left) && isFullNumberOrBooleanSpeculationExpectingDefined(right))
changed |= mergePrediction(SpecInt32Only);
else
changed |= mergePrediction(node->getHeapPrediction());
}
break;
}
case ValueAdd: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
&& isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
changed |= mergePrediction(SpecInt32Only);
else if (m_graph.addShouldSpeculateInt52(node))
changed |= mergePrediction(SpecInt52Any);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
} else if (isStringOrStringObjectSpeculation(left) || isStringOrStringObjectSpeculation(right)) {
// left or right is definitely something other than a number.
changed |= mergePrediction(SpecString);
} else if (isBigIntSpeculation(left) && isBigIntSpeculation(right))
changed |= mergePrediction(SpecBigInt);
else {
changed |= mergePrediction(SpecInt32Only);
if (node->mayHaveDoubleResult())
changed |= mergePrediction(SpecBytecodeDouble);
if (node->mayHaveBigIntResult())
changed |= mergePrediction(SpecBigInt);
if (node->mayHaveNonNumericResult())
changed |= mergePrediction(SpecString);
}
}
break;
}
case ArithAdd: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
changed |= mergePrediction(SpecInt32Only);
else if (m_graph.addShouldSpeculateInt52(node))
changed |= mergePrediction(SpecInt52Any);
else if (isFullNumberOrBooleanSpeculation(left) && isFullNumberOrBooleanSpeculation(right))
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
else if (node->mayHaveNonIntResult() || (left & SpecBytecodeDouble) || (right & SpecBytecodeDouble))
changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
else
changed |= mergePrediction(SpecInt32Only);
}
break;
}
case ArithSub: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
&& isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
changed |= mergePrediction(SpecInt32Only);
else if (m_graph.addShouldSpeculateInt52(node))
changed |= mergePrediction(SpecInt52Any);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
} else if (node->mayHaveNonIntResult() || (left & SpecBytecodeDouble) || (right & SpecBytecodeDouble))
changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
else
changed |= mergePrediction(SpecInt32Only);
}
break;
}
case ValueSub: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
&& isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
changed |= mergePrediction(SpecInt32Only);
else if (m_graph.addShouldSpeculateInt52(node))
changed |= mergePrediction(SpecInt52Any);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
} else if (isBigIntSpeculation(left) && isBigIntSpeculation(right))
changed |= mergePrediction(SpecBigInt);
else {
changed |= mergePrediction(SpecInt32Only);
if (node->mayHaveDoubleResult())
changed |= mergePrediction(SpecBytecodeDouble);
if (node->mayHaveBigIntResult())
changed |= mergePrediction(SpecBigInt);
}
}
break;
}
case ValuePow: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (node->child1()->shouldSpeculateBigInt() && node->child2()->shouldSpeculateBigInt())
changed |= mergePrediction(SpecBigInt);
else if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
&& isFullNumberOrBooleanSpeculationExpectingDefined(right))
changed |= mergePrediction(SpecBytecodeDouble);
else
changed |= mergePrediction(SpecBytecodeDouble | SpecBigInt);
}
break;
}
case ValueNegate:
case ArithNegate: {
SpeculatedType prediction = node->child1()->prediction();
if (prediction) {
if (isInt32OrBooleanSpeculation(prediction) && node->canSpeculateInt32(m_pass))
changed |= mergePrediction(SpecInt32Only);
else if (m_graph.unaryArithShouldSpeculateInt52(node, m_pass))
changed |= mergePrediction(SpecInt52Any);
else if (isBytecodeNumberSpeculation(prediction))
changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction()));
else {
changed |= mergePrediction(SpecInt32Only);
if (node->op() == ValueNegate && node->mayHaveBigIntResult())
changed |= mergePrediction(SpecBigInt);
if (node->mayHaveDoubleResult())
changed |= mergePrediction(SpecBytecodeDouble);
}
}
break;
}
case ArithMin:
case ArithMax: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (Node::shouldSpeculateInt32OrBooleanForArithmetic(node->child1().node(), node->child2().node())
&& node->canSpeculateInt32(m_pass))
changed |= mergePrediction(SpecInt32Only);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
}
break;
}
case ValueMul:
case ArithMul: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
// FIXME: We're currently relying on prediction propagation and backwards propagation
// whenever we can, and only falling back on result flags if that fails. And the result
// flags logic doesn't know how to use backwards propagation. We should get rid of the
// prediction propagation logic and rely solely on the result type.
if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
&& isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
if (m_graph.binaryArithShouldSpeculateInt32(node, m_pass))
changed |= mergePrediction(SpecInt32Only);
else if (m_graph.binaryArithShouldSpeculateInt52(node, m_pass))
changed |= mergePrediction(SpecInt52Any);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
} else if (op == ValueMul && isBigIntSpeculation(left) && isBigIntSpeculation(right))
changed |= mergePrediction(SpecBigInt);
else {
changed |= mergePrediction(SpecInt32Only);
if (node->mayHaveDoubleResult()
|| (left & SpecBytecodeDouble)
|| (right & SpecBytecodeDouble))
changed |= mergePrediction(SpecBytecodeDouble);
if ((op == ValueMul && node->mayHaveBigIntResult())
|| (left & SpecBigInt)
|| (right & SpecBigInt))
changed |= mergePrediction(SpecBigInt);
}
}
break;
}
case ValueDiv:
case ValueMod:
case ArithDiv:
case ArithMod: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
&& isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
if (m_graph.binaryArithShouldSpeculateInt32(node, m_pass))
changed |= mergePrediction(SpecInt32Only);
else
changed |= mergePrediction(SpecBytecodeDouble);
} else if ((op == ValueDiv || op == ValueMod) && isBigIntSpeculation(left) && isBigIntSpeculation(right))
changed |= mergePrediction(SpecBigInt);
else {
changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
if ((op == ValueDiv || op == ValueMod) && (node->mayHaveBigIntResult()
|| (left & SpecBigInt)
|| (right & SpecBigInt)))
changed |= mergePrediction(SpecBigInt);
}
}
break;
}
case ArithAbs: {
SpeculatedType childPrediction = node->child1()->prediction();
if (isInt32OrBooleanSpeculation(childPrediction)
&& node->canSpeculateInt32(m_pass))
changed |= mergePrediction(SpecInt32Only);
else
changed |= mergePrediction(SpecBytecodeDouble);
break;
}
case GetByVal:
case AtomicsAdd:
case AtomicsAnd:
case AtomicsCompareExchange:
case AtomicsExchange:
case AtomicsLoad:
case AtomicsOr:
case AtomicsStore:
case AtomicsSub:
case AtomicsXor: {
Edge child1 = m_graph.child(node, 0);
if (!child1->prediction())
break;
Edge child2 = m_graph.child(node, 1);
ArrayMode arrayMode = node->arrayMode().refine(
m_graph, node,
child1->prediction(),
child2->prediction(),
SpecNone);
switch (arrayMode.type()) {
case Array::Int32:
if (arrayMode.isOutOfBounds())
changed |= mergePrediction(node->getHeapPrediction() | SpecInt32Only);
else
changed |= mergePrediction(SpecInt32Only);
break;
case Array::Double:
if (arrayMode.isOutOfBounds())
changed |= mergePrediction(node->getHeapPrediction() | SpecDoubleReal);
else if (node->getHeapPrediction() & SpecNonIntAsDouble)
changed |= mergePrediction(SpecDoubleReal);
else
changed |= mergePrediction(SpecAnyIntAsDouble);
break;
case Array::Float32Array:
case Array::Float64Array:
changed |= mergePrediction(SpecFullDouble);
break;
case Array::Uint32Array:
if (isInt32SpeculationForArithmetic(node->getHeapPrediction()) && node->op() == GetByVal)
changed |= mergePrediction(SpecInt32Only);
else if (enableInt52())
changed |= mergePrediction(SpecInt52Any);
else
changed |= mergePrediction(SpecInt32Only | SpecAnyIntAsDouble);
break;
case Array::Int8Array:
case Array::Uint8Array:
case Array::Int16Array:
case Array::Uint16Array:
case Array::Int32Array:
changed |= mergePrediction(SpecInt32Only);
break;
default:
changed |= mergePrediction(node->getHeapPrediction());
break;
}
break;
}
case ToThis: {
// ToThis in methods for primitive types should speculate primitive types in strict mode.
bool isStrictMode = m_graph.isStrictModeFor(node->origin.semantic);
if (isStrictMode) {
if (node->child1()->shouldSpeculateBoolean()) {
changed |= mergePrediction(SpecBoolean);
break;
}
if (node->child1()->shouldSpeculateInt32()) {
changed |= mergePrediction(SpecInt32Only);
break;
}
if (node->child1()->shouldSpeculateInt52()) {
changed |= mergePrediction(SpecInt52Any);
break;
}
if (node->child1()->shouldSpeculateNumber()) {
changed |= mergePrediction(SpecBytecodeNumber);
break;
}
if (node->child1()->shouldSpeculateSymbol()) {
changed |= mergePrediction(SpecSymbol);
break;
}
if (node->child1()->shouldSpeculateBigInt()) {
changed |= mergePrediction(SpecBigInt);
break;
}
if (node->child1()->shouldSpeculateStringIdent()) {
changed |= mergePrediction(SpecStringIdent);
break;
}
if (node->child1()->shouldSpeculateString()) {
changed |= mergePrediction(SpecString);
break;
}
} else {
if (node->child1()->shouldSpeculateString()) {
changed |= mergePrediction(SpecStringObject);
break;
}
}
SpeculatedType prediction = node->child1()->prediction();
if (isStrictMode)
changed |= mergePrediction(node->getHeapPrediction());
else if (prediction) {
if (prediction & ~SpecObject) {
// Wrapper objects are created only in sloppy mode.
prediction &= SpecObject;
prediction = mergeSpeculations(prediction, SpecObjectOther);
}
changed |= mergePrediction(prediction);
}
break;
}
case ToPrimitive: {
SpeculatedType child = node->child1()->prediction();
if (child)
changed |= mergePrediction(resultOfToPrimitive(child));
break;
}
case NormalizeMapKey: {
SpeculatedType prediction = node->child1()->prediction();
if (prediction)
changed |= mergePrediction(prediction);
break;
}
default:
break;
}
m_changed |= changed;
}
void propagateForward()
{
for (Node* node : m_dependentNodes) {
m_currentNode = node;
propagate(m_currentNode);
}
}
void propagateBackward()
{
for (unsigned i = m_dependentNodes.size(); i--;) {
m_currentNode = m_dependentNodes[i];
propagate(m_currentNode);
}
}
void doDoubleVoting(Node* node, float weight)
{
// Loop pre-headers created by OSR entrypoint creation may have NaN weight to indicate
// that we actually don't know they weight. Assume that they execute once. This turns
// out to be an OK assumption since the pre-header doesn't have any meaningful code.
if (weight != weight)
weight = 1;
switch (node->op()) {
case ValueAdd:
case ValueSub:
case ArithAdd:
case ArithSub: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
DoubleBallot ballot;
if (isFullNumberSpeculation(left)
&& isFullNumberSpeculation(right)
&& !m_graph.addShouldSpeculateInt32(node, m_pass)
&& !m_graph.addShouldSpeculateInt52(node))
ballot = VoteDouble;
else
ballot = VoteValue;
m_graph.voteNode(node->child1(), ballot, weight);
m_graph.voteNode(node->child2(), ballot, weight);
break;
}
case ValueMul:
case ArithMul: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
DoubleBallot ballot;
if (isFullNumberSpeculation(left)
&& isFullNumberSpeculation(right)
&& !m_graph.binaryArithShouldSpeculateInt32(node, m_pass)
&& !m_graph.binaryArithShouldSpeculateInt52(node, m_pass))
ballot = VoteDouble;
else
ballot = VoteValue;
m_graph.voteNode(node->child1(), ballot, weight);
m_graph.voteNode(node->child2(), ballot, weight);
break;
}
case ArithMin:
case ArithMax:
case ArithMod:
case ValueDiv:
case ValueMod:
case ArithDiv: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
DoubleBallot ballot;
if (isFullNumberSpeculation(left)
&& isFullNumberSpeculation(right)
&& !m_graph.binaryArithShouldSpeculateInt32(node, m_pass))
ballot = VoteDouble;
else
ballot = VoteValue;
m_graph.voteNode(node->child1(), ballot, weight);
m_graph.voteNode(node->child2(), ballot, weight);
break;
}
case ArithAbs:
DoubleBallot ballot;
if (node->child1()->shouldSpeculateNumber()
&& !m_graph.unaryArithShouldSpeculateInt32(node, m_pass))
ballot = VoteDouble;
else
ballot = VoteValue;
m_graph.voteNode(node->child1(), ballot, weight);
break;
case ArithSqrt:
case ArithUnary:
if (node->child1()->shouldSpeculateNumber())
m_graph.voteNode(node->child1(), VoteDouble, weight);
else
m_graph.voteNode(node->child1(), VoteValue, weight);
break;
case SetLocal: {
SpeculatedType prediction = node->child1()->prediction();
if (isDoubleSpeculation(prediction))
node->variableAccessData()->vote(VoteDouble, weight);
else if (!isFullNumberSpeculation(prediction) || isInt32OrInt52Speculation(prediction))
node->variableAccessData()->vote(VoteValue, weight);
break;
}
case PutByValDirect:
case PutByVal:
case PutByValAlias: {
Edge child1 = m_graph.varArgChild(node, 0);
Edge child2 = m_graph.varArgChild(node, 1);
Edge child3 = m_graph.varArgChild(node, 2);
m_graph.voteNode(child1, VoteValue, weight);
m_graph.voteNode(child2, VoteValue, weight);
switch (node->arrayMode().type()) {
case Array::Double:
m_graph.voteNode(child3, VoteDouble, weight);
break;
default:
m_graph.voteNode(child3, VoteValue, weight);
break;
}
break;
}
case DataViewSet: {
DataViewData data = node->dataViewData();
if (data.isFloatingPoint)
m_graph.voteNode(m_graph.varArgChild(node, 2), VoteValue, weight);
break;
}
case MovHint:
// Ignore these since they have no effect on in-DFG execution.
break;
default:
m_graph.voteChildren(node, VoteValue, weight);
break;
}
}
void doRoundOfDoubleVoting()
{
for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i)
m_graph.m_variableAccessData[i].find()->clearVotes();
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
BasicBlock* block = m_graph.block(blockIndex);
if (!block)
continue;
ASSERT(block->isReachable);
for (unsigned i = 0; i < block->size(); ++i) {
m_currentNode = block->at(i);
doDoubleVoting(m_currentNode, block->executionCount);
}
}
for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
if (!variableAccessData->isRoot())
continue;
m_changed |= variableAccessData->tallyVotesForShouldUseDoubleFormat();
}
propagateThroughArgumentPositions();
for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
if (!variableAccessData->isRoot())
continue;
m_changed |= variableAccessData->makePredictionForDoubleFormat();
}
}
void propagateThroughArgumentPositions()
{
for (unsigned i = 0; i < m_graph.m_argumentPositions.size(); ++i)
m_changed |= m_graph.m_argumentPositions[i].mergeArgumentPredictionAwareness();
}
// Sets any predictions that do not depends on other nodes.
void processInvariants()
{
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
for (Node* node : *block) {
m_currentNode = node;
processInvariantsForNode();
}
}
}
void processInvariantsForNode()
{
switch (m_currentNode->op()) {
case JSConstant: {
SpeculatedType type = speculationFromValue(m_currentNode->asJSValue());
if (type == SpecAnyIntAsDouble && enableInt52())
type = int52AwareSpeculationFromValue(m_currentNode->asJSValue());
setPrediction(type);
break;
}
case DoubleConstant: {
SpeculatedType type = speculationFromValue(m_currentNode->asJSValue());
setPrediction(type);
break;
}
case ArithBitNot:
case ArithBitAnd:
case ArithBitOr:
case ArithBitXor:
case BitRShift:
case ArithBitLShift:
case BitURShift:
case ArithIMul:
case ArithClz32: {
setPrediction(SpecInt32Only);
break;
}
case ArrayPop:
case ArrayPush:
case RegExpExec:
case RegExpExecNonGlobalOrSticky:
case RegExpTest:
case RegExpMatchFast:
case RegExpMatchFastGlobal:
case StringReplace:
case StringReplaceRegExp:
case GetById:
case GetByIdFlush:
case GetByIdWithThis:
case GetByIdDirect:
case GetByIdDirectFlush:
case TryGetById:
case GetByValWithThis:
case GetByOffset:
case MultiGetByOffset:
case GetDirectPname:
case Call:
case DirectCall:
case TailCallInlinedCaller:
case DirectTailCallInlinedCaller:
case Construct:
case DirectConstruct:
case CallVarargs:
case CallEval:
case TailCallVarargsInlinedCaller:
case ConstructVarargs:
case CallForwardVarargs:
case ConstructForwardVarargs:
case TailCallForwardVarargsInlinedCaller:
case GetGlobalVar:
case GetGlobalLexicalVariable:
case GetClosureVar:
case GetFromArguments:
case LoadKeyFromMapBucket:
case LoadValueFromMapBucket:
case ToNumber:
case ToObject:
case ValueBitAnd:
case ValueBitXor:
case ValueBitOr:
case ValueBitNot:
case CallObjectConstructor:
case GetArgument:
case CallDOMGetter:
case GetDynamicVar:
case GetPrototypeOf:
case ExtractValueFromWeakMapGet:
case DataViewGetInt:
case DataViewGetFloat: {
setPrediction(m_currentNode->getHeapPrediction());
break;
}
case WeakMapGet:
case ResolveScopeForHoistingFuncDeclInEval: {
setPrediction(SpecBytecodeTop);
break;
}
case GetGetterSetterByOffset:
case GetExecutable: {
setPrediction(SpecCellOther);
break;
}
case GetGetter:
case GetSetter:
case GetCallee:
case NewFunction:
case NewGeneratorFunction:
case NewAsyncGeneratorFunction:
case NewAsyncFunction: {
setPrediction(SpecFunction);
break;
}
case GetArgumentCountIncludingThis: {
setPrediction(SpecInt32Only);
break;
}
case SetCallee:
case SetArgumentCountIncludingThis:
break;
case MapHash:
setPrediction(SpecInt32Only);
break;
case GetMapBucket:
case GetMapBucketHead:
case GetMapBucketNext:
case SetAdd:
case MapSet:
setPrediction(SpecCellOther);
break;
case GetRestLength:
case ArrayIndexOf: {
setPrediction(SpecInt32Only);
break;
}
case GetTypedArrayByteOffset:
case GetArrayLength:
case GetVectorLength: {
setPrediction(SpecInt32Only);
break;
}
case StringCharCodeAt: {
setPrediction(SpecInt32Only);
break;
}
case StringValueOf:
case StringSlice:
case ToLowerCase:
setPrediction(SpecString);
break;
case ArithPow:
case ArithSqrt:
case ArithFRound:
case ArithUnary: {
setPrediction(SpecBytecodeDouble);
break;
}
case ArithRound:
case ArithFloor:
case ArithCeil:
case ArithTrunc: {
if (isInt32OrBooleanSpeculation(m_currentNode->getHeapPrediction())
&& m_graph.roundShouldSpeculateInt32(m_currentNode, m_pass))
setPrediction(SpecInt32Only);
else
setPrediction(SpecBytecodeDouble);
break;
}
case ArithRandom: {
setPrediction(SpecDoubleReal);
break;
}
case DeleteByVal:
case DeleteById:
case LogicalNot:
case CompareLess:
case CompareLessEq:
case CompareGreater:
case CompareGreaterEq:
case CompareBelow:
case CompareBelowEq:
case CompareEq:
case CompareStrictEq:
case CompareEqPtr:
case SameValue:
case OverridesHasInstance:
case InstanceOf:
case InstanceOfCustom:
case IsEmpty:
case IsUndefined:
case IsUndefinedOrNull:
case IsBoolean:
case IsNumber:
case NumberIsInteger:
case IsObject:
case IsObjectOrNull:
case IsFunction:
case IsCellWithType:
case IsTypedArrayView:
case MatchStructure: {
setPrediction(SpecBoolean);
break;
}
case TypeOf: {
setPrediction(SpecStringIdent);
break;
}
case GetButterfly:
case GetIndexedPropertyStorage:
case AllocatePropertyStorage:
case ReallocatePropertyStorage: {
setPrediction(SpecOther);
break;
}
case CheckSubClass:
break;
case SkipScope:
case GetGlobalObject: {
setPrediction(SpecObjectOther);
break;
}
case GetGlobalThis:
setPrediction(SpecObject);
break;
case ResolveScope: {
setPrediction(SpecObjectOther);
break;
}
case ObjectCreate:
case CreateThis:
case NewObject: {
setPrediction(SpecFinalObject);
break;
}
case ArraySlice:
case NewArrayWithSpread:
case NewArray:
case NewArrayWithSize:
case CreateRest:
case NewArrayBuffer:
case ObjectKeys: {
setPrediction(SpecArray);
break;
}
case Spread:
setPrediction(SpecCellOther);
break;
case NewTypedArray: {
setPrediction(speculationFromTypedArrayType(m_currentNode->typedArrayType()));
break;
}
case NewRegexp: {
setPrediction(SpecRegExpObject);
break;
}
case PushWithScope:
case CreateActivation: {
setPrediction(SpecObjectOther);
break;
}
case StringFromCharCode: {
setPrediction(SpecString);
m_currentNode->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsInt);
break;
}
case StringCharAt:
case CallStringConstructor:
case ToString:
case NumberToStringWithRadix:
case NumberToStringWithValidRadixConstant:
case MakeRope:
case StrCat: {
setPrediction(SpecString);
break;
}
case NewStringObject: {
setPrediction(SpecStringObject);
break;
}
case NewSymbol: {
setPrediction(SpecSymbol);
break;
}
case CreateDirectArguments: {
setPrediction(SpecDirectArguments);
break;
}
case CreateScopedArguments: {
setPrediction(SpecScopedArguments);
break;
}
case CreateClonedArguments: {
setPrediction(SpecObjectOther);
break;
}
case FiatInt52: {
RELEASE_ASSERT(enableInt52());
setPrediction(SpecInt52Any);
break;
}
case GetScope:
setPrediction(SpecObjectOther);
break;
case InByVal:
case InById:
setPrediction(SpecBoolean);
break;
case HasOwnProperty:
setPrediction(SpecBoolean);
break;
case GetEnumerableLength: {
setPrediction(SpecInt32Only);
break;
}
case HasGenericProperty:
case HasStructureProperty:
case HasIndexedProperty: {
setPrediction(SpecBoolean);
break;
}
case GetPropertyEnumerator: {
setPrediction(SpecCell);
break;
}
case GetEnumeratorStructurePname: {
setPrediction(SpecCell | SpecOther);
break;
}
case GetEnumeratorGenericPname: {
setPrediction(SpecCell | SpecOther);
break;
}
case ToIndexString: {
setPrediction(SpecString);
break;
}
case ParseInt: {
// We expect this node to almost always produce an int32. However,
// it's possible it produces NaN or integers out of int32 range. We
// rely on the heap prediction since the parseInt() call profiled
// its result.
setPrediction(m_currentNode->getHeapPrediction());
break;
}
case IdentityWithProfile: {
setPrediction(m_currentNode->getForcedPrediction());
break;
}
case ExtractCatchLocal: {
setPrediction(m_currentNode->catchLocalPrediction());
break;
}
case GetLocal:
case SetLocal:
case UInt32ToNumber:
case ValueNegate:
case ValueAdd:
case ValueSub:
case ValueMul:
case ValueDiv:
case ValueMod:
case ValuePow:
case ValueBitLShift:
case ArithAdd:
case ArithSub:
case ArithNegate:
case ArithMin:
case ArithMax:
case ArithMul:
case ArithDiv:
case ArithMod:
case ArithAbs:
case GetByVal:
case ToThis:
case ToPrimitive:
case NormalizeMapKey:
case AtomicsAdd:
case AtomicsAnd:
case AtomicsCompareExchange:
case AtomicsExchange:
case AtomicsLoad:
case AtomicsOr:
case AtomicsStore:
case AtomicsSub:
case AtomicsXor: {
m_dependentNodes.append(m_currentNode);
break;
}
case AtomicsIsLockFree: {
setPrediction(SpecBoolean);
break;
}
case CPUIntrinsic: {
if (m_currentNode->intrinsic() == CPURdtscIntrinsic)
setPrediction(SpecInt32Only);
else
setPrediction(SpecOther);
break;
}
case PutByValAlias:
case DoubleAsInt32:
case CheckArray:
case CheckTypeInfoFlags:
case Arrayify:
case ArrayifyToStructure:
case CheckTierUpInLoop:
case CheckTierUpAtReturn:
case CheckTierUpAndOSREnter:
case CheckInBounds:
case ValueToInt32:
case DoubleRep:
case ValueRep:
case Int52Rep:
case Int52Constant:
case Identity:
case BooleanToNumber:
case PhantomNewObject:
case PhantomNewFunction:
case PhantomNewGeneratorFunction:
case PhantomNewAsyncGeneratorFunction:
case PhantomNewAsyncFunction:
case PhantomCreateActivation:
case PhantomDirectArguments:
case PhantomCreateRest:
case PhantomSpread:
case PhantomNewArrayWithSpread:
case PhantomNewArrayBuffer:
case PhantomClonedArguments:
case PhantomNewRegexp:
case GetMyArgumentByVal:
case GetMyArgumentByValOutOfBounds:
case PutHint:
case CheckStructureImmediate:
case CheckStructureOrEmpty:
case MaterializeNewObject:
case MaterializeCreateActivation:
case PutStack:
case KillStack:
case StoreBarrier:
case FencedStoreBarrier:
case GetStack:
case GetRegExpObjectLastIndex:
case SetRegExpObjectLastIndex:
case RecordRegExpCachedResult:
case LazyJSConstant:
case CallDOM: {
// This node should never be visible at this stage of compilation.
DFG_CRASH(m_graph, m_currentNode, "Unexpected node during prediction propagation");
break;
}
case Phi:
// Phis should not be visible here since we're iterating the all-but-Phi's
// part of basic blocks.
RELEASE_ASSERT_NOT_REACHED();
break;
case EntrySwitch:
case Upsilon:
// These don't get inserted until we go into SSA.
RELEASE_ASSERT_NOT_REACHED();
break;
#ifndef NDEBUG
// These get ignored because they don't return anything.
case PutByValDirect:
case PutByValWithThis:
case PutByIdWithThis:
case PutByVal:
case PutClosureVar:
case PutToArguments:
case Return:
case Throw:
case ThrowStaticError:
case TailCall:
case DirectTailCall:
case TailCallVarargs:
case TailCallForwardVarargs:
case PutById:
case PutByIdFlush:
case PutByIdDirect:
case PutByOffset:
case MultiPutByOffset:
case PutGetterById:
case PutSetterById:
case PutGetterSetterById:
case PutGetterByVal:
case PutSetterByVal:
case DefineDataProperty:
case DefineAccessorProperty:
case DFG::Jump:
case Branch:
case Switch:
case ProfileType:
case ProfileControlFlow:
case ForceOSRExit:
case SetArgumentDefinitely:
case SetArgumentMaybe:
case SetFunctionName:
case CheckStructure:
case CheckCell:
case CheckNotEmpty:
case AssertNotEmpty:
case CheckStringIdent:
case CheckBadCell:
case PutStructure:
case Phantom:
case Check:
case CheckVarargs:
case PutGlobalVariable:
case CheckTraps:
case LogShadowChickenPrologue:
case LogShadowChickenTail:
case Unreachable:
case LoopHint:
case NotifyWrite:
case ConstantStoragePointer:
case MovHint:
case ZombieHint:
case ExitOK:
case LoadVarargs:
case ForwardVarargs:
case PutDynamicVar:
case NukeStructureAndSetButterfly:
case InitializeEntrypointArguments:
case WeakSetAdd:
case WeakMapSet:
case FilterCallLinkStatus:
case FilterGetByIdStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
case ClearCatchLocals:
case DataViewSet:
case InvalidationPoint:
break;
// This gets ignored because it only pretends to produce a value.
case BottomValue:
break;
// This gets ignored because it already has a prediction.
case ExtractOSREntryLocal:
break;
// These gets ignored because it doesn't do anything.
case CountExecution:
case SuperSamplerBegin:
case SuperSamplerEnd:
case PhantomLocal:
case Flush:
break;
case LastNodeType:
RELEASE_ASSERT_NOT_REACHED();
break;
#else
default:
break;
#endif
}
}
SpeculatedType resultOfToPrimitive(SpeculatedType type)
{
if (type & SpecObject) {
// We try to be optimistic here about StringObjects since it's unlikely that
// someone overrides the valueOf or toString methods.
if (type & SpecStringObject && m_graph.canOptimizeStringObjectAccess(m_currentNode->origin.semantic))
return mergeSpeculations(type & ~SpecObject, SpecString);
return mergeSpeculations(type & ~SpecObject, SpecPrimitive);
}
return type;
}
Vector<Node*> m_dependentNodes;
Node* m_currentNode;
bool m_changed { false };
PredictionPass m_pass { PrimaryPass }; // We use different logic for considering predictions depending on how far along we are in propagation.
};
} // Anonymous namespace.
bool performPredictionPropagation(Graph& graph)
{
return runPhase<PredictionPropagationPhase>(graph);
}
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)