Commit 94e84e9b authored by fpizlo@apple.com's avatar fpizlo@apple.com
Browse files

DFG should optimize out the NaN check on loads from double arrays if the array...

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):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@134168 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent c6ca925e
2012-11-10 Filip Pizlo <fpizlo@apple.com>
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 <fpizlo@apple.com>
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
......
......@@ -922,7 +922,9 @@ bool AbstractState::execute(unsigned indexInBlock)
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:
......
......@@ -409,6 +409,8 @@ const char* arrayClassToString(Array::Class arrayClass)
const char* arraySpeculationToString(Array::Speculation speculation)
{
switch (speculation) {
case Array::SaneChain:
return "SaneChain";
case Array::InBounds:
return "InBounds";
case Array::ToHole:
......
......@@ -86,9 +86,10 @@ enum Class {
};
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 @@ public:
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
......
......@@ -1703,7 +1703,7 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins
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 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins
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);
......
......@@ -134,6 +134,16 @@ private:
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:
......
......@@ -112,6 +112,12 @@ const char* nodeFlagsAsString(NodeFlags flags)
ptr.strcat("PureNum");
hasPrinted = true;
}
if (flags & NodeUsedAsOther) {
if (hasPrinted)
ptr.strcat("|");
ptr.strcat("UseAsOther");
hasPrinted = true;
}
}
if (flags & NodeMayOverflow) {
......
......@@ -52,14 +52,15 @@ namespace JSC { namespace DFG {
#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;
......
......@@ -184,7 +184,7 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
changed |= mergePrediction(speculatedDoubleTypeForPrediction(m_graph[node.child1()].prediction()));
}
flags &= ~NodeUsedAsOther;
changed |= m_graph[node.child1()].mergeFlags(flags);
break;
......@@ -323,6 +329,8 @@ private:
}
flags |= NodeUsedAsNumber;
flags &= ~NodeUsedAsOther;
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
break;
......@@ -345,6 +353,8 @@ private:
// 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 @@ private:
// 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 @@ private:
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 @@ private:
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 @@ private:
else
changed |= mergePrediction(speculatedDoubleTypeForPrediction(child));
flags &= ~NodeNeedsNegZero;
changed |= m_graph[node.child1()].mergeFlags(flags);
break;
}
......@@ -448,13 +463,13 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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;
......
......@@ -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 @@ void SpeculativeJIT::compile(Node& node)
}
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 @@ void SpeculativeJIT::compile(Node& node)
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;
}
......
......@@ -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 @@ void SpeculativeJIT::compile(Node& node)
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 @@ void SpeculativeJIT::compile(Node& node)
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;
}
......
......@@ -437,6 +437,14 @@ void JSGlobalObject::haveABadTime(JSGlobalData& globalData)
}
}
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);
......
......@@ -333,6 +333,8 @@ namespace JSC {
}
void haveABadTime(JSGlobalData&);
bool arrayPrototypeChainIsSane();
void setProfileGroup(unsigned value) { createRareDataIfNeeded(); m_rareData->profileGroup = value; }
unsigned profileGroup() const
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment