/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "DFGBasicBlockInlines.h"
#include "DFGGraph.h"
#include "DFGPhase.h"
-#include "Operations.h"
+#include "JSCInlines.h"
namespace JSC { namespace DFG {
bool run()
{
- for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
- BasicBlock* block = m_graph.m_blocks[blockIndex].get();
- if (!block)
- continue;
-
- // Prevent a tower of overflowing additions from creating a value that is out of the
- // safe 2^48 range.
- m_allowNestedOverflowingAdditions = block->size() < (1 << 16);
-
- for (unsigned indexInBlock = block->size(); indexInBlock--;)
- propagate(block->at(indexInBlock));
+ m_changed = true;
+ while (m_changed) {
+ m_changed = false;
+ for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
+ BasicBlock* block = m_graph.block(blockIndex);
+ if (!block)
+ continue;
+
+ // Prevent a tower of overflowing additions from creating a value that is out of the
+ // safe 2^48 range.
+ m_allowNestedOverflowingAdditions = block->size() < (1 << 16);
+
+ for (unsigned indexInBlock = block->size(); indexInBlock--;)
+ propagate(block->at(indexInBlock));
+ }
}
return true;
private:
bool isNotNegZero(Node* node)
{
- if (!m_graph.isNumberConstant(node))
+ if (!node->isNumberConstant())
return false;
- double value = m_graph.valueOfNumberConstant(node);
+ double value = node->asNumber();
return (value || 1.0 / value > 0.0);
}
bool isNotPosZero(Node* node)
{
- if (!m_graph.isNumberConstant(node))
+ if (!node->isNumberConstant())
return false;
- double value = m_graph.valueOfNumberConstant(node);
+ double value = node->asNumber();
return (value || 1.0 / value < 0.0);
}
template<int power>
bool isWithinPowerOfTwoForConstant(Node* node)
{
- JSValue immediateValue = node->valueOfJSConstant(codeBlock());
+ JSValue immediateValue = node->asJSValue();
if (!immediateValue.isNumber())
return false;
double immediate = immediateValue.asNumber();
template<int power>
bool isWithinPowerOfTwoNonRecursive(Node* node)
{
- if (node->op() != JSConstant)
+ if (!node->isNumberConstant())
return false;
return isWithinPowerOfTwoForConstant<power>(node);
}
bool isWithinPowerOfTwo(Node* node)
{
switch (node->op()) {
- case JSConstant: {
+ case DoubleConstant:
+ case JSConstant:
+ case Int52Constant: {
return isWithinPowerOfTwoForConstant<power>(node);
}
case BitOr:
case BitXor:
- case BitLShift:
- case ValueToInt32: {
+ case BitLShift: {
return power > 31;
}
return true;
Node* shiftAmount = node->child2().node();
- if (shiftAmount->op() != JSConstant)
+ if (!node->isNumberConstant())
return false;
- JSValue immediateValue = shiftAmount->valueOfJSConstant(codeBlock());
+ JSValue immediateValue = shiftAmount->asJSValue();
if (!immediateValue.isInt32())
return false;
return immediateValue.asInt32() > 32 - power;
childIdx < node->firstChild() + node->numChildren();
childIdx++) {
if (!!m_graph.m_varArgChildren[childIdx])
- changed |= m_graph.m_varArgChildren[childIdx]->mergeFlags(NodeUsedAsValue);
+ changed |= m_graph.m_varArgChildren[childIdx]->mergeFlags(NodeBytecodeUsesAsValue);
}
} else {
if (!node->child1())
return changed;
- changed |= node->child1()->mergeFlags(NodeUsedAsValue);
+ changed |= node->child1()->mergeFlags(NodeBytecodeUsesAsValue);
if (!node->child2())
return changed;
- changed |= node->child2()->mergeFlags(NodeUsedAsValue);
+ changed |= node->child2()->mergeFlags(NodeBytecodeUsesAsValue);
if (!node->child3())
return changed;
- changed |= node->child3()->mergeFlags(NodeUsedAsValue);
+ changed |= node->child3()->mergeFlags(NodeBytecodeUsesAsValue);
}
return changed;
}
void propagate(Node* node)
{
- NodeFlags flags = node->flags() & NodeBackPropMask;
+ NodeFlags flags = node->flags() & NodeBytecodeBackPropMask;
switch (node->op()) {
case GetLocal: {
VariableAccessData* variableAccessData = node->variableAccessData();
- variableAccessData->mergeFlags(flags);
+ flags &= ~NodeBytecodeUsesAsInt; // We don't care about cross-block uses-as-int.
+ m_changed |= variableAccessData->mergeFlags(flags);
break;
}
VariableAccessData* variableAccessData = node->variableAccessData();
if (!variableAccessData->isLoadedFrom())
break;
- node->child1()->mergeFlags(NodeUsedAsValue);
+ flags = variableAccessData->flags();
+ RELEASE_ASSERT(!(flags & ~NodeBytecodeBackPropMask));
+ flags |= NodeBytecodeUsesAsNumber; // Account for the fact that control flow may cause overflows that our modeling can't handle.
+ node->child1()->mergeFlags(flags);
break;
}
+ case Flush: {
+ VariableAccessData* variableAccessData = node->variableAccessData();
+ m_changed |= variableAccessData->mergeFlags(NodeBytecodeUsesAsValue);
+ break;
+ }
+
+ case MovHint:
+ case Check:
+ break;
+
case BitAnd:
case BitOr:
case BitXor:
case BitLShift:
case BitURShift:
case ArithIMul: {
- flags |= NodeUsedAsInt;
- flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther);
+ flags |= NodeBytecodeUsesAsInt;
+ flags &= ~(NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero | NodeBytecodeUsesAsOther);
+ flags &= ~NodeBytecodeUsesAsArrayIndex;
node->child1()->mergeFlags(flags);
node->child2()->mergeFlags(flags);
break;
}
- case ValueToInt32: {
- flags |= NodeUsedAsInt;
- flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther);
- node->child1()->mergeFlags(flags);
- break;
- }
-
case StringCharCodeAt: {
- node->child1()->mergeFlags(NodeUsedAsValue);
- node->child2()->mergeFlags(NodeUsedAsValue | NodeUsedAsInt);
+ node->child1()->mergeFlags(NodeBytecodeUsesAsValue);
+ node->child2()->mergeFlags(NodeBytecodeUsesAsValue | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex);
break;
}
- case Identity:
case UInt32ToNumber: {
node->child1()->mergeFlags(flags);
break;
case ValueAdd: {
if (isNotNegZero(node->child1().node()) || isNotNegZero(node->child2().node()))
- flags &= ~NodeNeedsNegZero;
+ flags &= ~NodeBytecodeNeedsNegZero;
if (node->child1()->hasNumberResult() || node->child2()->hasNumberResult())
- flags &= ~NodeUsedAsOther;
+ flags &= ~NodeBytecodeUsesAsOther;
if (!isWithinPowerOfTwo<32>(node->child1()) && !isWithinPowerOfTwo<32>(node->child2()))
- flags |= NodeUsedAsNumber;
+ flags |= NodeBytecodeUsesAsNumber;
if (!m_allowNestedOverflowingAdditions)
- flags |= NodeUsedAsNumber;
+ flags |= NodeBytecodeUsesAsNumber;
node->child1()->mergeFlags(flags);
node->child2()->mergeFlags(flags);
case ArithAdd: {
if (isNotNegZero(node->child1().node()) || isNotNegZero(node->child2().node()))
- flags &= ~NodeNeedsNegZero;
+ flags &= ~NodeBytecodeNeedsNegZero;
if (!isWithinPowerOfTwo<32>(node->child1()) && !isWithinPowerOfTwo<32>(node->child2()))
- flags |= NodeUsedAsNumber;
+ flags |= NodeBytecodeUsesAsNumber;
if (!m_allowNestedOverflowingAdditions)
- flags |= NodeUsedAsNumber;
+ flags |= NodeBytecodeUsesAsNumber;
node->child1()->mergeFlags(flags);
node->child2()->mergeFlags(flags);
break;
}
-
+
+ case ArithClz32: {
+ flags &= ~(NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero | NodeBytecodeUsesAsOther | ~NodeBytecodeUsesAsArrayIndex);
+ flags |= NodeBytecodeUsesAsInt;
+ node->child1()->mergeFlags(flags);
+ break;
+ }
+
case ArithSub: {
if (isNotNegZero(node->child1().node()) || isNotPosZero(node->child2().node()))
- flags &= ~NodeNeedsNegZero;
+ flags &= ~NodeBytecodeNeedsNegZero;
if (!isWithinPowerOfTwo<32>(node->child1()) && !isWithinPowerOfTwo<32>(node->child2()))
- flags |= NodeUsedAsNumber;
+ flags |= NodeBytecodeUsesAsNumber;
if (!m_allowNestedOverflowingAdditions)
- flags |= NodeUsedAsNumber;
+ flags |= NodeBytecodeUsesAsNumber;
node->child1()->mergeFlags(flags);
node->child2()->mergeFlags(flags);
}
case ArithNegate: {
- flags &= ~NodeUsedAsOther;
+ flags &= ~NodeBytecodeUsesAsOther;
node->child1()->mergeFlags(flags);
break;
if (!isWithinPowerOfTwo<22>(node->child1().node())
&& !isWithinPowerOfTwo<22>(node->child2().node()))
- flags |= NodeUsedAsNumber;
+ flags |= NodeBytecodeUsesAsNumber;
node->mergeFlags(flags);
- flags |= NodeUsedAsNumber | NodeNeedsNegZero;
- flags &= ~NodeUsedAsOther;
+ flags |= NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero;
+ flags &= ~NodeBytecodeUsesAsOther;
node->child1()->mergeFlags(flags);
node->child2()->mergeFlags(flags);
}
case ArithDiv: {
- flags |= NodeUsedAsNumber | NodeNeedsNegZero;
- flags &= ~NodeUsedAsOther;
+ flags |= NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero;
+ flags &= ~NodeBytecodeUsesAsOther;
node->child1()->mergeFlags(flags);
node->child2()->mergeFlags(flags);
}
case ArithMod: {
- flags |= NodeUsedAsNumber | NodeNeedsNegZero;
- flags &= ~NodeUsedAsOther;
+ flags |= NodeBytecodeUsesAsNumber;
+ flags &= ~NodeBytecodeUsesAsOther;
node->child1()->mergeFlags(flags);
- node->child2()->mergeFlags(flags);
+ node->child2()->mergeFlags(flags & ~NodeBytecodeNeedsNegZero);
break;
}
case GetByVal: {
- node->child1()->mergeFlags(NodeUsedAsValue);
- node->child2()->mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
+ node->child1()->mergeFlags(NodeBytecodeUsesAsValue);
+ node->child2()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex);
break;
}
- case GetMyArgumentByValSafe: {
- node->child1()->mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
+ case NewArrayWithSize: {
+ node->child1()->mergeFlags(NodeBytecodeUsesAsValue | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex);
break;
}
- case NewArrayWithSize: {
- node->child1()->mergeFlags(NodeUsedAsValue | NodeUsedAsInt);
+ case NewTypedArray: {
+ // Negative zero is not observable. NaN versus undefined are only observable
+ // in that you would get a different exception message. So, like, whatever: we
+ // claim here that NaN v. undefined is observable.
+ node->child1()->mergeFlags(NodeBytecodeUsesAsInt | NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeUsesAsArrayIndex);
break;
}
case StringCharAt: {
- node->child1()->mergeFlags(NodeUsedAsValue);
- node->child2()->mergeFlags(NodeUsedAsValue | NodeUsedAsInt);
+ node->child1()->mergeFlags(NodeBytecodeUsesAsValue);
+ node->child2()->mergeFlags(NodeBytecodeUsesAsValue | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex);
break;
}
- case ToString: {
- node->child1()->mergeFlags(NodeUsedAsNumber | NodeUsedAsOther);
+ case ToString:
+ case CallStringConstructor: {
+ node->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther);
break;
}
node->child1()->mergeFlags(flags);
break;
}
-
+
+ case PutByValDirect:
case PutByVal: {
- m_graph.varArgChild(node, 0)->mergeFlags(NodeUsedAsValue);
- m_graph.varArgChild(node, 1)->mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
- m_graph.varArgChild(node, 2)->mergeFlags(NodeUsedAsValue);
+ m_graph.varArgChild(node, 0)->mergeFlags(NodeBytecodeUsesAsValue);
+ m_graph.varArgChild(node, 1)->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex);
+ m_graph.varArgChild(node, 2)->mergeFlags(NodeBytecodeUsesAsValue);
+ break;
+ }
+
+ case Switch: {
+ SwitchData* data = node->switchData();
+ switch (data->kind) {
+ case SwitchImm:
+ // We don't need NodeBytecodeNeedsNegZero because if the cases are all integers
+ // then -0 and 0 are treated the same. We don't need NodeBytecodeUsesAsOther
+ // because if all of the cases are integers then NaN and undefined are
+ // treated the same (i.e. they will take default).
+ node->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsInt);
+ break;
+ case SwitchChar: {
+ // We don't need NodeBytecodeNeedsNegZero because if the cases are all strings
+ // then -0 and 0 are treated the same. We don't need NodeBytecodeUsesAsOther
+ // because if all of the cases are single-character strings then NaN
+ // and undefined are treated the same (i.e. they will take default).
+ node->child1()->mergeFlags(NodeBytecodeUsesAsNumber);
+ break;
+ }
+ case SwitchString:
+ // We don't need NodeBytecodeNeedsNegZero because if the cases are all strings
+ // then -0 and 0 are treated the same.
+ node->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther);
+ break;
+ case SwitchCell:
+ // There is currently no point to being clever here since this is used for switching
+ // on objects.
+ mergeDefaultFlags(node);
+ break;
+ }
break;
}
+
+ case Identity:
+ // This would be trivial to handle but we just assert that we cannot see these yet.
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+
+ // Note: ArithSqrt, ArithSin, and ArithCos and other math intrinsics don't have special
+ // rules in here because they are always followed by Phantoms to signify that if the
+ // method call speculation fails, the bytecode may use the arguments in arbitrary ways.
+ // This corresponds to that possibility of someone doing something like:
+ // Math.sin = function(x) { doArbitraryThingsTo(x); }
default:
mergeDefaultFlags(node);
}
bool m_allowNestedOverflowingAdditions;
+ bool m_changed;
};
bool performBackwardsPropagation(Graph& graph)