DFGPredictionPropagationPhase.cpp [plain text]
#include "config.h"
#include "DFGPredictionPropagationPhase.h"
#if ENABLE(DFG_JIT)
#include "DFGGraph.h"
#include "DFGPhase.h"
#include "Operations.h"
namespace JSC { namespace DFG {
SpeculatedType resultOfToPrimitive(SpeculatedType type)
{
if (type & SpecObject) {
return mergeSpeculations(type & ~SpecObject, SpecString);
}
return type;
}
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);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
m_count = 0;
#endif
do {
m_changed = false;
propagateForward();
if (!m_changed)
break;
m_changed = false;
propagateBackward();
} while (m_changed);
do {
m_changed = false;
doRoundOfDoubleVoting();
if (!m_changed)
break;
m_changed = false;
propagateForward();
} while (m_changed);
return true;
}
private:
bool setPrediction(SpeculatedType prediction)
{
ASSERT(m_currentNode->hasResult());
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)
{
if (!isNumberSpeculation(value))
return SpecDouble;
if (value & SpecDoubleNaN)
return SpecDouble;
return SpecDoubleReal;
}
SpeculatedType speculatedDoubleTypeForPredictions(SpeculatedType left, SpeculatedType right)
{
return speculatedDoubleTypeForPrediction(mergeSpeculations(left, right));
}
void propagate(Node* node)
{
NodeType op = node->op();
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(" ", Graph::opName(op), " ", m_currentNode, ": ", NodeFlagsDump(node->flags()), " ");
#endif
bool changed = false;
switch (op) {
case JSConstant:
case WeakJSConstant: {
changed |= setPrediction(speculationFromValue(m_graph.valueOfJSConstant(node)));
break;
}
case GetLocal: {
VariableAccessData* variableAccessData = node->variableAccessData();
SpeculatedType prediction = variableAccessData->prediction();
if (prediction)
changed |= mergePrediction(prediction);
break;
}
case SetLocal: {
VariableAccessData* variableAccessData = node->variableAccessData();
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 ValueToInt32: {
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 GetScopedVar:
case Resolve:
case ResolveBase:
case ResolveBaseStrictPut:
case ResolveGlobal: {
changed |= setPrediction(node->getHeapPrediction());
break;
}
case StringCharCodeAt: {
changed |= setPrediction(SpecInt32);
break;
}
case UInt32ToNumber: {
if (nodeCanSpeculateInteger(node->arithNodeFlags()))
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(SpecNumber);
break;
}
case ValueAdd: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (isNumberSpeculationExpectingDefined(left) && isNumberSpeculationExpectingDefined(right)) {
if (m_graph.addSpeculationMode(node) != DontSpeculateInteger)
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
} else if (!(left & SpecNumber) || !(right & SpecNumber)) {
changed |= mergePrediction(SpecString);
} else
changed |= mergePrediction(SpecString | SpecInt32 | SpecDouble);
}
break;
}
case ArithAdd: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (m_graph.addSpeculationMode(node) != DontSpeculateInteger)
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
}
break;
}
case ArithSub: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (m_graph.addSpeculationMode(node) != DontSpeculateInteger)
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
}
break;
}
case ArithNegate:
if (node->child1()->prediction()) {
if (m_graph.negateShouldSpeculateInteger(node))
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction()));
}
break;
case ArithMin:
case ArithMax: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (Node::shouldSpeculateIntegerForArithmetic(node->child1().node(), node->child2().node())
&& nodeCanSpeculateInteger(node->arithNodeFlags()))
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
}
break;
}
case ArithMul: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (m_graph.mulShouldSpeculateInteger(node))
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
}
break;
}
case ArithDiv: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (Node::shouldSpeculateIntegerForArithmetic(node->child1().node(), node->child2().node())
&& nodeCanSpeculateInteger(node->arithNodeFlags()))
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(SpecDouble);
}
break;
}
case ArithMod: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
if (Node::shouldSpeculateIntegerForArithmetic(node->child1().node(), node->child2().node())
&& nodeCanSpeculateInteger(node->arithNodeFlags()))
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(SpecDouble);
}
break;
}
case ArithSqrt: {
changed |= setPrediction(SpecDouble);
break;
}
case ArithAbs: {
SpeculatedType child = node->child1()->prediction();
if (isInt32SpeculationForArithmetic(child)
&& nodeCanSpeculateInteger(node->arithNodeFlags()))
changed |= mergePrediction(SpecInt32);
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);
break;
}
case GetByVal: {
if (node->child1()->shouldSpeculateFloat32Array()
|| node->child1()->shouldSpeculateFloat64Array())
changed |= mergePrediction(SpecDouble);
else
changed |= mergePrediction(node->getHeapPrediction());
break;
}
case GetMyArgumentsLengthSafe: {
changed |= setPrediction(SpecInt32);
break;
}
case GetScopeRegisters:
case GetButterfly:
case GetIndexedPropertyStorage:
case AllocatePropertyStorage:
case ReallocatePropertyStorage: {
changed |= setPrediction(SpecOther);
break;
}
case ConvertThis: {
SpeculatedType prediction = node->child1()->prediction();
if (prediction) {
if (prediction & ~SpecObject) {
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 NewRegexp:
case CreateActivation: {
changed |= setPrediction(SpecObjectOther);
break;
}
case StringFromCharCode: {
changed |= setPrediction(SpecString);
changed |= node->child1()->mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
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);
break;
}
case CreateArguments: {
changed |= setPrediction(SpecArguments);
break;
}
case NewFunction: {
SpeculatedType child = node->child1()->prediction();
if (child & SpecEmpty)
changed |= mergePrediction((child & ~SpecEmpty) | SpecFunction);
else
changed |= mergePrediction(child);
break;
}
case NewFunctionNoCheck:
case NewFunctionExpression: {
changed |= setPrediction(SpecFunction);
break;
}
case PutByValAlias:
case GetArrayLength:
case Int32ToDouble:
case ForwardInt32ToDouble:
case DoubleAsInt32:
case GetLocalUnlinked:
case GetMyArgumentsLength:
case GetMyArgumentByVal:
case PhantomPutStructure:
case PhantomArguments:
case CheckArray:
case Arrayify:
case ArrayifyToStructure:
case MovHint:
case MovHintAndCheck:
case ZombieHint: {
CRASH();
break;
}
case Phi:
CRASH();
break;
case GetScope:
changed |= setPrediction(SpecObjectOther);
break;
case Identity:
changed |= mergePrediction(node->child1()->prediction());
break;
#ifndef NDEBUG
case PutByVal:
case PutScopedVar:
case Return:
case Throw:
case PutById:
case PutByIdDirect:
case PutByOffset:
case SetCallee:
case SetMyScope:
case DFG::Jump:
case Branch:
case Breakpoint:
case CheckHasInstance:
case ThrowReferenceError:
case ForceOSRExit:
case SetArgument:
case CheckStructure:
case CheckExecutable:
case ForwardCheckStructure:
case StructureTransitionWatchpoint:
case ForwardStructureTransitionWatchpoint:
case CheckFunction:
case PutStructure:
case TearOffActivation:
case TearOffArguments:
case CheckArgumentsNotCreated:
case GlobalVarWatchpoint:
case GarbageValue:
case AllocationProfileWatchpoint:
case Phantom:
case PutGlobalVar:
case PutGlobalVarCheck:
case CheckWatchdogTimer:
case Unreachable:
break;
case InlineStart:
case Nop:
case CountExecution:
case PhantomLocal:
case Flush:
break;
case LastNodeType:
CRASH();
break;
#else
default:
break;
#endif
}
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(SpeculationDump(node->prediction()), "\n");
#endif
m_changed |= changed;
}
void propagateForward()
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLogF("Propagating predictions forward [%u]\n", ++m_count);
#endif
for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
BasicBlock* block = m_graph.m_blocks[blockIndex].get();
if (!block)
continue;
ASSERT(block->isReachable);
for (unsigned i = 0; i < block->size(); ++i) {
m_currentNode = block->at(i);
propagate(m_currentNode);
}
}
}
void propagateBackward()
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLogF("Propagating predictions backward [%u]\n", ++m_count);
#endif
for (BlockIndex blockIndex = m_graph.m_blocks.size(); blockIndex--;) {
BasicBlock* block = m_graph.m_blocks[blockIndex].get();
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 (isNumberSpeculationExpectingDefined(left) && isNumberSpeculationExpectingDefined(right)
&& !m_graph.addShouldSpeculateInteger(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 (isNumberSpeculation(left) && isNumberSpeculation(right)
&& !m_graph.mulShouldSpeculateInteger(node))
ballot = VoteDouble;
else
ballot = VoteValue;
m_graph.voteNode(node->child1(), ballot);
m_graph.voteNode(node->child2(), ballot);
break;
}
case ArithMin:
case ArithMax:
case ArithMod:
case ArithDiv: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
DoubleBallot ballot;
if (isNumberSpeculation(left) && isNumberSpeculation(right)
&& !(Node::shouldSpeculateIntegerForArithmetic(node->child1().node(), node->child2().node()) && node->canSpeculateInteger()))
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()->shouldSpeculateIntegerForArithmetic() && node->canSpeculateInteger()))
ballot = VoteDouble;
else
ballot = VoteValue;
m_graph.voteNode(node->child1(), ballot);
break;
case ArithSqrt:
m_graph.voteNode(node->child1(), VoteDouble);
break;
case SetLocal: {
SpeculatedType prediction = node->child1()->prediction();
if (isDoubleSpeculation(prediction))
node->variableAccessData()->vote(VoteDouble);
else if (!isNumberSpeculation(prediction) || isInt32Speculation(prediction))
node->variableAccessData()->vote(VoteValue);
break;
}
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;
}
default:
m_graph.voteChildren(node, VoteValue);
break;
}
}
void doRoundOfDoubleVoting()
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLogF("Voting on double uses of locals [%u]\n", m_count);
#endif
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.m_blocks.size(); ++blockIndex) {
BasicBlock* block = m_graph.m_blocks[blockIndex].get();
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();
}
}
Node* m_currentNode;
bool m_changed;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
unsigned m_count;
#endif
};
bool performPredictionPropagation(Graph& graph)
{
SamplingRegion samplingRegion("DFG Prediction Propagation Phase");
return runPhase<PredictionPropagationPhase>(graph);
}
} }
#endif // ENABLE(DFG_JIT)