Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (134167 => 134168)
--- trunk/Source/_javascript_Core/ChangeLog 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/ChangeLog 2012-11-11 02:56:12 UTC (rev 134168)
@@ -1,5 +1,49 @@
2012-11-10 Filip Pizlo <[email protected]>
+ DFG should optimize out the NaN check on loads from double arrays if the array prototype chain is having a great time
+ https://bugs.webkit.org/show_bug.cgi?id=101718
+
+ Reviewed by Geoffrey Garen.
+
+ If we're reading from a JSArray in double mode, where the array's structure is
+ primordial (all aspects of the structure are unchanged except for indexing type),
+ and the result of the load is used in arithmetic that is known to not distinguish
+ between NaN and undefined, then we should not emit a NaN check. Looks like a 5%
+ win on navier-stokes.
+
+ Also fixed an OpInfo initialization goof for String ops that was revealed by this
+ change.
+
+ * dfg/DFGAbstractState.cpp:
+ (JSC::DFG::AbstractState::execute):
+ * dfg/DFGArrayMode.cpp:
+ (JSC::DFG::arraySpeculationToString):
+ * dfg/DFGArrayMode.h:
+ (JSC::DFG::ArrayMode::isSaneChain):
+ (ArrayMode):
+ (JSC::DFG::ArrayMode::isInBounds):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleIntrinsic):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNodeFlags.cpp:
+ (JSC::DFG::nodeFlagsAsString):
+ * dfg/DFGNodeFlags.h:
+ (DFG):
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::arrayPrototypeChainIsSane):
+ (JSC):
+ * runtime/JSGlobalObject.h:
+ (JSGlobalObject):
+
+2012-11-10 Filip Pizlo <[email protected]>
+
DFG constant folding and CFG simplification should be smart enough to know that if a logical op's operand is proven to have a non-masquerading structure then it always evaluates to true
https://bugs.webkit.org/show_bug.cgi?id=101511
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractState.cpp (134167 => 134168)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractState.cpp 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractState.cpp 2012-11-11 02:56:12 UTC (rev 134168)
@@ -922,7 +922,9 @@
if (node.arrayMode().isOutOfBounds()) {
clobberWorld(node.codeOrigin, indexInBlock);
forNode(nodeIndex).makeTop();
- } else
+ } else if (node.arrayMode().isSaneChain())
+ forNode(nodeIndex).set(SpecDouble);
+ else
forNode(nodeIndex).set(SpecDoubleReal);
break;
case Array::Contiguous:
Modified: trunk/Source/_javascript_Core/dfg/DFGArrayMode.cpp (134167 => 134168)
--- trunk/Source/_javascript_Core/dfg/DFGArrayMode.cpp 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/dfg/DFGArrayMode.cpp 2012-11-11 02:56:12 UTC (rev 134168)
@@ -409,6 +409,8 @@
const char* arraySpeculationToString(Array::Speculation speculation)
{
switch (speculation) {
+ case Array::SaneChain:
+ return "SaneChain";
case Array::InBounds:
return "InBounds";
case Array::ToHole:
Modified: trunk/Source/_javascript_Core/dfg/DFGArrayMode.h (134167 => 134168)
--- trunk/Source/_javascript_Core/dfg/DFGArrayMode.h 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/dfg/DFGArrayMode.h 2012-11-11 02:56:12 UTC (rev 134168)
@@ -86,9 +86,10 @@
};
enum Speculation {
- InBounds,
- ToHole,
- OutOfBounds
+ SaneChain, // In bounds and the array prototype chain is still intact, i.e. loading a hole doesn't require special treatment.
+ InBounds, // In bounds and not loading a hole.
+ ToHole, // Potentially storing to a hole.
+ OutOfBounds // Out-of-bounds access and anything can happen.
};
enum Conversion {
AsIs,
@@ -223,9 +224,20 @@
return arrayClass() == Array::OriginalArray;
}
+ bool isSaneChain() const
+ {
+ return speculation() == Array::SaneChain;
+ }
+
bool isInBounds() const
{
- return speculation() == Array::InBounds;
+ switch (speculation()) {
+ case Array::SaneChain:
+ case Array::InBounds:
+ return true;
+ default:
+ return false;
+ }
}
bool mayStoreToHole() const
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (134167 => 134168)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2012-11-11 02:56:12 UTC (rev 134168)
@@ -1703,7 +1703,7 @@
int thisOperand = registerOffset + argumentToOperand(0);
int indexOperand = registerOffset + argumentToOperand(1);
- NodeIndex charCode = addToGraph(StringCharCodeAt, OpInfo(Array::String), get(thisOperand), getToInt32(indexOperand));
+ NodeIndex charCode = addToGraph(StringCharCodeAt, OpInfo(ArrayMode(Array::String).asWord()), get(thisOperand), getToInt32(indexOperand));
if (usesResult)
set(resultOperand, charCode);
@@ -1716,7 +1716,7 @@
int thisOperand = registerOffset + argumentToOperand(0);
int indexOperand = registerOffset + argumentToOperand(1);
- NodeIndex charCode = addToGraph(StringCharAt, OpInfo(Array::String), get(thisOperand), getToInt32(indexOperand));
+ NodeIndex charCode = addToGraph(StringCharAt, OpInfo(ArrayMode(Array::String).asWord()), get(thisOperand), getToInt32(indexOperand));
if (usesResult)
set(resultOperand, charCode);
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (134167 => 134168)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2012-11-11 02:56:12 UTC (rev 134168)
@@ -134,6 +134,16 @@
m_graph[node.child2()].prediction()));
blessArrayOperation(node.child1(), node.child2(), 2);
+
+ ArrayMode arrayMode = node.arrayMode();
+ if (arrayMode.type() == Array::Double
+ && arrayMode.arrayClass() == Array::OriginalArray
+ && arrayMode.speculation() == Array::InBounds
+ && arrayMode.conversion() == Array::AsIs
+ && m_graph.globalObjectFor(node.codeOrigin)->arrayPrototypeChainIsSane()
+ && !(node.flags() & NodeUsedAsOther))
+ node.setArrayMode(arrayMode.withSpeculation(Array::SaneChain));
+
break;
}
case StringCharAt:
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeFlags.cpp (134167 => 134168)
--- trunk/Source/_javascript_Core/dfg/DFGNodeFlags.cpp 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeFlags.cpp 2012-11-11 02:56:12 UTC (rev 134168)
@@ -112,6 +112,12 @@
ptr.strcat("PureNum");
hasPrinted = true;
}
+ if (flags & NodeUsedAsOther) {
+ if (hasPrinted)
+ ptr.strcat("|");
+ ptr.strcat("UseAsOther");
+ hasPrinted = true;
+ }
}
if (flags & NodeMayOverflow) {
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeFlags.h (134167 => 134168)
--- trunk/Source/_javascript_Core/dfg/DFGNodeFlags.h 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeFlags.h 2012-11-11 02:56:12 UTC (rev 134168)
@@ -52,14 +52,15 @@
#define NodeMayOverflow 0x100
#define NodeMayNegZero 0x200
-#define NodeBackPropMask 0x1C00
+#define NodeBackPropMask 0x3C00
#define NodeUseBottom 0x000
#define NodeUsedAsNumber 0x400 // The result of this computation may be used in a context that observes fractional results.
#define NodeNeedsNegZero 0x800 // The result of this computation may be used in a context that observes -0.
-#define NodeUsedAsValue (NodeUsedAsNumber | NodeNeedsNegZero)
-#define NodeUsedAsInt 0x1000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values.
+#define NodeUsedAsOther 0x1000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined).
+#define NodeUsedAsValue (NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther)
+#define NodeUsedAsInt 0x2000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values.
-#define NodeDoesNotExit 0x2000 // This flag is negated to make it natural for the default to be that a node does exit.
+#define NodeDoesNotExit 0x4000 // This flag is negated to make it natural for the default to be that a node does exit.
typedef uint16_t NodeFlags;
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (134167 => 134168)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2012-11-11 02:56:12 UTC (rev 134168)
@@ -184,7 +184,7 @@
case BitURShift: {
changed |= setPrediction(SpecInt32);
flags |= NodeUsedAsInt;
- flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero);
+ flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther);
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
break;
@@ -193,7 +193,7 @@
case ValueToInt32: {
changed |= setPrediction(SpecInt32);
flags |= NodeUsedAsInt;
- flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero);
+ flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther);
changed |= m_graph[node.child1()].mergeFlags(flags);
break;
}
@@ -221,7 +221,7 @@
case StringCharCodeAt: {
changed |= mergePrediction(SpecInt32);
changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
- changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
break;
}
@@ -254,6 +254,8 @@
if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index()))
flags &= ~NodeNeedsNegZero;
+ if (m_graph[node.child1()].hasNumberResult() || m_graph[node.child2()].hasNumberResult())
+ flags &= ~NodeUsedAsOther;
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
@@ -273,6 +275,7 @@
if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index()))
flags &= ~NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
@@ -292,6 +295,7 @@
if (isNotZero(node.child1().index()) || isNotZero(node.child2().index()))
flags &= ~NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
@@ -306,6 +310,8 @@
changed |= mergePrediction(speculatedDoubleTypeForPrediction(m_graph[node.child1()].prediction()));
}
+ flags &= ~NodeUsedAsOther;
+
changed |= m_graph[node.child1()].mergeFlags(flags);
break;
@@ -323,6 +329,8 @@
}
flags |= NodeUsedAsNumber;
+ flags &= ~NodeUsedAsOther;
+
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
break;
@@ -345,6 +353,8 @@
// no matter what, and always forces its inputs to check as well.
flags |= NodeUsedAsNumber | NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
+
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
break;
@@ -368,6 +378,8 @@
// no matter what, and always forces its inputs to check as well.
flags |= NodeUsedAsNumber | NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
+
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
break;
@@ -385,7 +397,9 @@
changed |= mergePrediction(SpecDouble);
}
- flags |= NodeUsedAsValue;
+ flags |= NodeUsedAsNumber | NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
+
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
break;
@@ -393,7 +407,9 @@
case ArithSqrt: {
changed |= setPrediction(SpecDouble);
- changed |= m_graph[node.child1()].mergeFlags(flags | NodeUsedAsValue);
+ flags |= NodeUsedAsNumber | NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
+ changed |= m_graph[node.child1()].mergeFlags(flags);
break;
}
@@ -405,7 +421,6 @@
else
changed |= mergePrediction(speculatedDoubleTypeForPrediction(child));
- flags &= ~NodeNeedsNegZero;
changed |= m_graph[node.child1()].mergeFlags(flags);
break;
}
@@ -448,13 +463,13 @@
changed |= mergePrediction(node.getHeapPrediction());
changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
- changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
break;
}
case GetMyArgumentByValSafe: {
changed |= mergePrediction(node.getHeapPrediction());
- changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
break;
}
@@ -555,7 +570,7 @@
case NewArrayWithSize: {
changed |= setPrediction(SpecArray);
- changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue | NodeUsedAsInt);
break;
}
@@ -572,7 +587,7 @@
case StringCharAt: {
changed |= setPrediction(SpecString);
changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
- changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
break;
}
@@ -581,7 +596,7 @@
for (unsigned childIdx = node.firstChild();
childIdx < node.firstChild() + node.numChildren();
++childIdx)
- changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsNumber);
+ changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther);
break;
}
@@ -646,7 +661,7 @@
case PutByVal:
changed |= m_graph[m_graph.varArgChild(node, 0)].mergeFlags(NodeUsedAsValue);
- changed |= m_graph[m_graph.varArgChild(node, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ changed |= m_graph[m_graph.varArgChild(node, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
changed |= m_graph[m_graph.varArgChild(node, 2)].mergeFlags(NodeUsedAsValue);
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (134167 => 134168)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2012-11-11 02:56:12 UTC (rev 134168)
@@ -29,9 +29,11 @@
#if ENABLE(DFG_JIT)
+#include "ArrayPrototype.h"
#include "DFGCallArrayAllocatorSlowPathGenerator.h"
#include "DFGSlowPathGenerator.h"
#include "JSActivation.h"
+#include "ObjectPrototype.h"
namespace JSC { namespace DFG {
@@ -2702,6 +2704,13 @@
}
case Array::Double: {
if (node.arrayMode().isInBounds()) {
+ if (node.arrayMode().isSaneChain()) {
+ JSGlobalObject* globalObject = m_jit.globalObjectFor(node.codeOrigin);
+ ASSERT(globalObject->arrayPrototypeChainIsSane());
+ globalObject->arrayPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint());
+ globalObject->objectPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint());
+ }
+
SpeculateStrictInt32Operand property(this, node.child2());
StorageOperand storage(this, node.child3());
@@ -2715,7 +2724,8 @@
FPRTemporary result(this);
m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.fpr());
- speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, result.fpr(), result.fpr()));
+ if (!node.arrayMode().isSaneChain())
+ speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, result.fpr(), result.fpr()));
doubleResult(result.fpr(), m_compileIndex);
break;
}
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (134167 => 134168)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2012-11-11 02:56:12 UTC (rev 134168)
@@ -29,8 +29,10 @@
#if ENABLE(DFG_JIT)
#include "Arguments.h"
+#include "ArrayPrototype.h"
#include "DFGCallArrayAllocatorSlowPathGenerator.h"
#include "DFGSlowPathGenerator.h"
+#include "ObjectPrototype.h"
namespace JSC { namespace DFG {
@@ -2654,6 +2656,13 @@
case Array::Double: {
if (node.arrayMode().isInBounds()) {
+ if (node.arrayMode().isSaneChain()) {
+ JSGlobalObject* globalObject = m_jit.globalObjectFor(node.codeOrigin);
+ ASSERT(globalObject->arrayPrototypeChainIsSane());
+ globalObject->arrayPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint());
+ globalObject->objectPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint());
+ }
+
SpeculateStrictInt32Operand property(this, node.child2());
StorageOperand storage(this, node.child3());
@@ -2667,7 +2676,8 @@
FPRTemporary result(this);
m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.fpr());
- speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, result.fpr(), result.fpr()));
+ if (!node.arrayMode().isSaneChain())
+ speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, result.fpr(), result.fpr()));
doubleResult(result.fpr(), m_compileIndex);
break;
}
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (134167 => 134168)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2012-11-11 02:56:12 UTC (rev 134168)
@@ -437,6 +437,14 @@
}
}
+bool JSGlobalObject::arrayPrototypeChainIsSane()
+{
+ return !hasIndexedProperties(m_arrayPrototype->structure()->indexingType())
+ && m_arrayPrototype->prototype() == m_objectPrototype.get()
+ && !hasIndexedProperties(m_objectPrototype->structure()->indexingType())
+ && m_objectPrototype->prototype().isNull();
+}
+
void JSGlobalObject::createThrowTypeError(ExecState* exec)
{
JSFunction* thrower = JSFunction::create(exec, this, 0, String(), globalFuncThrowTypeError);
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (134167 => 134168)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h 2012-11-11 00:55:37 UTC (rev 134167)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h 2012-11-11 02:56:12 UTC (rev 134168)
@@ -333,6 +333,8 @@
}
void haveABadTime(JSGlobalData&);
+
+ bool arrayPrototypeChainIsSane();
void setProfileGroup(unsigned value) { createRareDataIfNeeded(); m_rareData->profileGroup = value; }
unsigned profileGroup() const