Commit 99f3762d authored by fpizlo@apple.com's avatar fpizlo@apple.com

DFG should be able to emit effectful structure checks

https://bugs.webkit.org/show_bug.cgi?id=99260

Reviewed by Oliver Hunt.

This change allows us to find out if an array access that has gone polymorphic
is operating over known structures - i.e. the primordial array structures of the
global object that the code block containing the array access belongs to. We
term this state "OriginalArray" for short. The fact that the access has gone
polymorphic means that the array profile will not be able to report the set of
structures it had seen - but if it can tell us that all of the structures were
primordial then it just so happens that we can deduce what the structure set
would have been by just querying the code block's global object. This allows us
to emit an ArrayifyToStructure instead of an Arrayify if we find that we need to
do conversions. The fast path of an ArrayifyToStructure is exactly like the fast
path of a CheckStructure and is mostly subject to the same optimizations. It
also burns one fewer registers.
        
Essentially the notion of OriginalArray is a super cheap way of getting the
array profile to tell us a structure set instead of a singleton structure.
Currently, the array profile can only tell us the structure seen at an array
access if there was exactly one structure. If there were multiple structures, it
won't tell us anything other than the array modes and other auxiliary profiling
data (whether there were stores to holes, for example). With OriginalArray, we
cheaply get a structure set if all of the structures were primordial for the
code block's global object, since in that case the array mode set (ArrayModes)
can directly tell us the structure set. In the future, we might consider adding
complete structure sets to the array profiles, but I suspect that we would hit
diminishing returns if we did so - it would only help if we have array accesses
that are both polymorphic and are cross-global-object accesses (rare) or if the
arrays had named properties or other structure transitions that are unrelated to
indexing type (also rare).
        
This also does away with Arrayify (and the new ArrayifyToStructure) returning
the butterfly pointer. This turns out to be faster and easier to CSE.
        
And, this also changes constant folding to be able to eliminate CheckStructure,
ForwardCheckStructure, and ArrayifyToStructure in addition to being able to
transform them into structure transition watchpoints. This is great for
ArrayifyToStructure because then CSE and CFA know that there is no side effect.
Converting CheckStructure and ForwardCheckStructure to also behave this way is
just a matter of elegance.
        
This has no performance impact right now. It's intended to alleviate some of the
regressions seen in the early implementation of
https://bugs.webkit.org/show_bug.cgi?id=98606.

* bytecode/ArrayProfile.cpp:
(JSC::ArrayProfile::computeUpdatedPrediction):
* bytecode/ArrayProfile.h:
(JSC):
(JSC::ArrayProfile::ArrayProfile):
(ArrayProfile):
(JSC::ArrayProfile::usesOriginalArrayStructures):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::updateAllPredictionsAndCountLiveness):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGArrayMode.cpp:
(JSC::DFG::ArrayMode::fromObserved):
(JSC::DFG::ArrayMode::alreadyChecked):
(JSC::DFG::arrayClassToString):
* dfg/DFGArrayMode.h:
(JSC::DFG::ArrayMode::withProfile):
(JSC::DFG::ArrayMode::isJSArray):
(ArrayMode):
(JSC::DFG::ArrayMode::isJSArrayWithOriginalStructure):
(JSC::DFG::ArrayMode::supportsLength):
(JSC::DFG::ArrayMode::arrayModesWithIndexingShape):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::getArrayMode):
(JSC::DFG::ByteCodeParser::getArrayModeAndEmitChecks):
(JSC::DFG::ByteCodeParser::handleGetByOffset):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::checkStructureElimination):
(JSC::DFG::CSEPhase::structureTransitionWatchpointElimination):
(JSC::DFG::CSEPhase::getPropertyStorageLoadElimination):
(JSC::DFG::CSEPhase::checkArrayElimination):
(JSC::DFG::CSEPhase::getScopeRegistersLoadElimination):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::checkArray):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasStructure):
(JSC::DFG::Node::hasArrayMode):
(JSC::DFG::Node::arrayMode):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::jumpSlowForUnwantedArrayMode):
(JSC::DFG::SpeculativeJIT::arrayify):
* dfg/DFGSpeculativeJIT.h:
(SpeculativeJIT):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::isOriginalArrayStructure):
* runtime/Structure.cpp:
(JSC::Structure::nonPropertyTransition):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@132759 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 6e1f0a37
2012-10-28 Filip Pizlo <fpizlo@apple.com>
DFG should be able to emit effectful structure checks
https://bugs.webkit.org/show_bug.cgi?id=99260
Reviewed by Oliver Hunt.
This change allows us to find out if an array access that has gone polymorphic
is operating over known structures - i.e. the primordial array structures of the
global object that the code block containing the array access belongs to. We
term this state "OriginalArray" for short. The fact that the access has gone
polymorphic means that the array profile will not be able to report the set of
structures it had seen - but if it can tell us that all of the structures were
primordial then it just so happens that we can deduce what the structure set
would have been by just querying the code block's global object. This allows us
to emit an ArrayifyToStructure instead of an Arrayify if we find that we need to
do conversions. The fast path of an ArrayifyToStructure is exactly like the fast
path of a CheckStructure and is mostly subject to the same optimizations. It
also burns one fewer registers.
Essentially the notion of OriginalArray is a super cheap way of getting the
array profile to tell us a structure set instead of a singleton structure.
Currently, the array profile can only tell us the structure seen at an array
access if there was exactly one structure. If there were multiple structures, it
won't tell us anything other than the array modes and other auxiliary profiling
data (whether there were stores to holes, for example). With OriginalArray, we
cheaply get a structure set if all of the structures were primordial for the
code block's global object, since in that case the array mode set (ArrayModes)
can directly tell us the structure set. In the future, we might consider adding
complete structure sets to the array profiles, but I suspect that we would hit
diminishing returns if we did so - it would only help if we have array accesses
that are both polymorphic and are cross-global-object accesses (rare) or if the
arrays had named properties or other structure transitions that are unrelated to
indexing type (also rare).
This also does away with Arrayify (and the new ArrayifyToStructure) returning
the butterfly pointer. This turns out to be faster and easier to CSE.
And, this also changes constant folding to be able to eliminate CheckStructure,
ForwardCheckStructure, and ArrayifyToStructure in addition to being able to
transform them into structure transition watchpoints. This is great for
ArrayifyToStructure because then CSE and CFA know that there is no side effect.
Converting CheckStructure and ForwardCheckStructure to also behave this way is
just a matter of elegance.
This has no performance impact right now. It's intended to alleviate some of the
regressions seen in the early implementation of
https://bugs.webkit.org/show_bug.cgi?id=98606.
* bytecode/ArrayProfile.cpp:
(JSC::ArrayProfile::computeUpdatedPrediction):
* bytecode/ArrayProfile.h:
(JSC):
(JSC::ArrayProfile::ArrayProfile):
(ArrayProfile):
(JSC::ArrayProfile::usesOriginalArrayStructures):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::updateAllPredictionsAndCountLiveness):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGArrayMode.cpp:
(JSC::DFG::ArrayMode::fromObserved):
(JSC::DFG::ArrayMode::alreadyChecked):
(JSC::DFG::arrayClassToString):
* dfg/DFGArrayMode.h:
(JSC::DFG::ArrayMode::withProfile):
(JSC::DFG::ArrayMode::isJSArray):
(ArrayMode):
(JSC::DFG::ArrayMode::isJSArrayWithOriginalStructure):
(JSC::DFG::ArrayMode::supportsLength):
(JSC::DFG::ArrayMode::arrayModesWithIndexingShape):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::getArrayMode):
(JSC::DFG::ByteCodeParser::getArrayModeAndEmitChecks):
(JSC::DFG::ByteCodeParser::handleGetByOffset):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::checkStructureElimination):
(JSC::DFG::CSEPhase::structureTransitionWatchpointElimination):
(JSC::DFG::CSEPhase::getPropertyStorageLoadElimination):
(JSC::DFG::CSEPhase::checkArrayElimination):
(JSC::DFG::CSEPhase::getScopeRegistersLoadElimination):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::checkArray):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasStructure):
(JSC::DFG::Node::hasArrayMode):
(JSC::DFG::Node::arrayMode):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::jumpSlowForUnwantedArrayMode):
(JSC::DFG::SpeculativeJIT::arrayify):
* dfg/DFGSpeculativeJIT.h:
(SpeculativeJIT):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::isOriginalArrayStructure):
* runtime/Structure.cpp:
(JSC::Structure::nonPropertyTransition):
2012-10-28 Filip Pizlo <fpizlo@apple.com>
There should not be blind spots in array length array profiling
......
......@@ -26,6 +26,7 @@
#include "config.h"
#include "ArrayProfile.h"
#include "CodeBlock.h"
#include <wtf/StringExtras.h>
namespace JSC {
......@@ -64,12 +65,14 @@ const char* arrayModesToString(ArrayModes arrayModes)
return result;
}
void ArrayProfile::computeUpdatedPrediction(OperationInProgress operation)
void ArrayProfile::computeUpdatedPrediction(CodeBlock* codeBlock, OperationInProgress operation)
{
if (m_lastSeenStructure) {
m_observedArrayModes |= arrayModeFromStructure(m_lastSeenStructure);
m_mayInterceptIndexedAccesses |=
m_lastSeenStructure->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero();
if (!codeBlock->globalObject()->isOriginalArrayStructure(m_lastSeenStructure))
m_usesOriginalArrayStructures = false;
if (!m_structureIsPolymorphic) {
if (!m_expectedStructure)
m_expectedStructure = m_lastSeenStructure;
......
......@@ -33,6 +33,7 @@
namespace JSC {
class CodeBlock;
class LLIntOffsetsExtractor;
// This is a bitfield where each bit represents an IndexingType that we have seen.
......@@ -87,6 +88,7 @@ public:
, m_structureIsPolymorphic(false)
, m_mayStoreToHole(false)
, m_mayInterceptIndexedAccesses(false)
, m_usesOriginalArrayStructures(true)
, m_observedArrayModes(0)
{
}
......@@ -98,6 +100,7 @@ public:
, m_structureIsPolymorphic(false)
, m_mayStoreToHole(false)
, m_mayInterceptIndexedAccesses(false)
, m_usesOriginalArrayStructures(true)
, m_observedArrayModes(0)
{
}
......@@ -113,7 +116,7 @@ public:
m_lastSeenStructure = structure;
}
void computeUpdatedPrediction(OperationInProgress operation = NoOperation);
void computeUpdatedPrediction(CodeBlock*, OperationInProgress = NoOperation);
Structure* expectedStructure() const { return m_expectedStructure; }
bool structureIsPolymorphic() const
......@@ -129,6 +132,8 @@ public:
bool mayStoreToHole() const { return m_mayStoreToHole; }
bool usesOriginalArrayStructures() const { return m_usesOriginalArrayStructures; }
private:
friend class LLIntOffsetsExtractor;
......@@ -138,6 +143,7 @@ private:
bool m_structureIsPolymorphic;
bool m_mayStoreToHole; // This flag may become overloaded to indicate other special cases that were encountered during array access, as it depends on indexing type. Since we currently have basically just one indexing type (two variants of ArrayStorage), this flag for now just means exactly what its name implies.
bool m_mayInterceptIndexedAccesses;
bool m_usesOriginalArrayStructures;
ArrayModes m_observedArrayModes;
};
......
......@@ -2725,7 +2725,7 @@ void CodeBlock::updateAllPredictionsAndCountLiveness(
// site also has a value profile site - so we already know whether or not it's
// live.
for (unsigned i = m_arrayProfiles.size(); i--;)
m_arrayProfiles[i].computeUpdatedPrediction(operation);
m_arrayProfiles[i].computeUpdatedPrediction(this, operation);
}
void CodeBlock::updateAllPredictions(OperationInProgress operation)
......
......@@ -1327,7 +1327,8 @@ bool AbstractState::execute(unsigned indexInBlock)
// the futurePossibleStructure set then the constant folding phase should
// turn this into a watchpoint instead.
StructureSet& set = node.structureSet();
if (value.m_futurePossibleStructure.isSubsetOf(set))
if (value.m_futurePossibleStructure.isSubsetOf(set)
|| value.m_currentKnownStructure.isSubsetOf(set))
m_foundConstants = true;
node.setCanExit(
!value.m_currentKnownStructure.isSubsetOf(set)
......@@ -1368,7 +1369,7 @@ bool AbstractState::execute(unsigned indexInBlock)
case GetButterfly:
case AllocatePropertyStorage:
case ReallocatePropertyStorage:
node.setCanExit(false);
node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
forNode(node.child1()).filter(SpecCell);
forNode(nodeIndex).clear(); // The result is not a JS value.
break;
......@@ -1439,12 +1440,23 @@ bool AbstractState::execute(unsigned indexInBlock)
forNode(node.child1()).filter(SpecCell);
if (node.child2())
forNode(node.child2()).filter(SpecInt32);
forNode(nodeIndex).clear();
clobberStructures(indexInBlock);
forNode(node.child1()).filterArrayModes(node.arrayMode().arrayModesThatPassFiltering());
m_haveStructures = true;
break;
}
case ArrayifyToStructure: {
AbstractValue& value = forNode(node.child1());
StructureSet set = node.structure();
if (value.m_futurePossibleStructure.isSubsetOf(set)
|| value.m_currentKnownStructure.isSubsetOf(set))
m_foundConstants = true;
node.setCanExit(true);
clobberStructures(indexInBlock);
value.filter(set);
m_haveStructures = true;
break;
}
case GetIndexedPropertyStorage: {
switch (node.arrayMode().type()) {
case Array::String:
......@@ -1460,13 +1472,13 @@ bool AbstractState::execute(unsigned indexInBlock)
break;
}
case GetByOffset:
node.setCanExit(false);
node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
forNode(node.child1()).filter(SpecCell);
forNode(nodeIndex).makeTop();
break;
case PutByOffset:
node.setCanExit(false);
node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
forNode(node.child1()).filter(SpecCell);
break;
......
......@@ -42,32 +42,32 @@ ArrayMode ArrayMode::fromObserved(ArrayProfile* profile, Array::Action action, b
return ArrayMode(Array::Contiguous, Array::NonArray, Array::OutOfBounds, Array::Convert); // FIXME: we don't know whether to go to contiguous or array storage. We're making a static guess here. In future we should use exit profiling for this.
return ArrayMode(Array::SelectUsingPredictions);
case asArrayModes(NonArrayWithContiguous):
return ArrayMode(Array::Contiguous, Array::NonArray, Array::AsIs).withSpeculation(profile, makeSafe);
return ArrayMode(Array::Contiguous, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(ArrayWithContiguous):
return ArrayMode(Array::Contiguous, Array::Array, Array::AsIs).withSpeculation(profile, makeSafe);
return ArrayMode(Array::Contiguous, Array::Array, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous):
return ArrayMode(Array::Contiguous, Array::PossiblyArray, Array::AsIs).withSpeculation(profile, makeSafe);
return ArrayMode(Array::Contiguous, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(NonArrayWithArrayStorage):
return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::AsIs).withSpeculation(profile, makeSafe);
return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(NonArrayWithSlowPutArrayStorage):
case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage):
return ArrayMode(Array::SlowPutArrayStorage, Array::NonArray, Array::AsIs).withSpeculation(profile, makeSafe);
return ArrayMode(Array::SlowPutArrayStorage, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(ArrayWithArrayStorage):
return ArrayMode(Array::ArrayStorage, Array::Array, Array::AsIs).withSpeculation(profile, makeSafe);
return ArrayMode(Array::ArrayStorage, Array::Array, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(ArrayWithSlowPutArrayStorage):
case asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage):
return ArrayMode(Array::SlowPutArrayStorage, Array::Array, Array::AsIs).withSpeculation(profile, makeSafe);
return ArrayMode(Array::SlowPutArrayStorage, Array::Array, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage):
return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::AsIs).withSpeculation(profile, makeSafe);
return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage):
case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage):
return ArrayMode(Array::SlowPutArrayStorage, Array::PossiblyArray, Array::AsIs).withSpeculation(profile, makeSafe);
return ArrayMode(Array::SlowPutArrayStorage, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage):
return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::Convert).withSpeculation(profile, makeSafe);
return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::Convert).withProfile(profile, makeSafe);
case asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage):
return ArrayMode(Array::ArrayStorage, Array::Array, Array::Convert).withSpeculation(profile, makeSafe);
return ArrayMode(Array::ArrayStorage, Array::Array, Array::Convert).withProfile(profile, makeSafe);
case asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage):
return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::Convert).withSpeculation(profile, makeSafe);
return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::Convert).withProfile(profile, makeSafe);
case asArrayModes(NonArray) | asArrayModes(NonArrayWithContiguous):
if (action == Array::Write && !profile->mayInterceptIndexedAccesses())
return ArrayMode(Array::Contiguous, Array::NonArray, Array::OutOfBounds, Array::Convert);
......@@ -162,7 +162,7 @@ bool ArrayMode::alreadyChecked(AbstractValue& value) const
return isStringSpeculation(value.m_type);
case Array::Contiguous:
if (arrayClass() == Array::Array) {
if (isJSArray()) {
if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithContiguous)))
return true;
return value.m_currentKnownStructure.hasSingleton()
......@@ -175,7 +175,7 @@ bool ArrayMode::alreadyChecked(AbstractValue& value) const
&& hasContiguous(value.m_currentKnownStructure.singleton()->indexingType());
case Array::ArrayStorage:
if (arrayClass() == Array::Array) {
if (isJSArray()) {
if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage)))
return true;
return value.m_currentKnownStructure.hasSingleton()
......@@ -188,7 +188,7 @@ bool ArrayMode::alreadyChecked(AbstractValue& value) const
&& hasFastArrayStorage(value.m_currentKnownStructure.singleton()->indexingType());
case Array::SlowPutArrayStorage:
if (arrayClass() == Array::Array) {
if (isJSArray()) {
if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage)))
return true;
return value.m_currentKnownStructure.hasSingleton()
......@@ -292,6 +292,8 @@ const char* arrayClassToString(Array::Class arrayClass)
switch (arrayClass) {
case Array::Array:
return "Array";
case Array::OriginalArray:
return "OriginalArray";
case Array::NonArray:
return "NonArray";
case Array::PossiblyArray:
......
......@@ -70,9 +70,10 @@ enum Type {
};
enum Class {
NonArray,
Array,
PossiblyArray
NonArray, // Definitely some object that is not a JSArray.
Array, // Definitely a JSArray, and may or may not have custom properties or have undergone some other bizarre transitions.
OriginalArray, // Definitely a JSArray, and still has one of the primordial JSArray structures for the global object that this code block (possibly inlined code block) belongs to.
PossiblyArray // Some object that may or may not be a JSArray.
};
enum Speculation {
......@@ -145,9 +146,27 @@ public:
return ArrayMode(type(), arrayClass(), speculation, conversion());
}
ArrayMode withSpeculation(ArrayProfile* profile, bool makeSafe) const
ArrayMode withProfile(ArrayProfile* profile, bool makeSafe) const
{
return withSpeculation(makeSafe ? Array::OutOfBounds : (profile->mayStoreToHole() ? Array::ToHole : Array::InBounds));
Array::Speculation mySpeculation;
Array::Class myArrayClass;
if (makeSafe)
mySpeculation = Array::OutOfBounds;
else if (profile->mayStoreToHole())
mySpeculation = Array::ToHole;
else
mySpeculation = Array::InBounds;
if (isJSArray()) {
if (profile->usesOriginalArrayStructures())
myArrayClass = Array::OriginalArray;
else
myArrayClass = Array::Array;
} else
myArrayClass = arrayClass();
return ArrayMode(type(), myArrayClass, mySpeculation, conversion());
}
ArrayMode refine(SpeculatedType base, SpeculatedType index) const;
......@@ -170,7 +189,18 @@ public:
bool isJSArray() const
{
return arrayClass() == Array::Array;
switch (arrayClass()) {
case Array::Array:
case Array::OriginalArray:
return true;
default:
return false;
}
}
bool isJSArrayWithOriginalStructure() const
{
return arrayClass() == Array::OriginalArray;
}
bool isInBounds() const
......@@ -250,7 +280,7 @@ public:
case Array::Contiguous:
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
return arrayClass() == Array::Array;
return isJSArray();
default:
return true;
}
......@@ -314,6 +344,7 @@ private:
case Array::NonArray:
return asArrayModes(shape);
case Array::Array:
case Array::OriginalArray:
return asArrayModes(shape | IsArray);
case Array::PossiblyArray:
return asArrayModes(shape) | asArrayModes(shape | IsArray);
......
......@@ -908,13 +908,13 @@ private:
ArrayMode getArrayMode(ArrayProfile* profile)
{
profile->computeUpdatedPrediction();
profile->computeUpdatedPrediction(m_inlineStackTop->m_codeBlock);
return ArrayMode::fromObserved(profile, Array::Read, false);
}
ArrayMode getArrayModeAndEmitChecks(ArrayProfile* profile, Array::Action action, NodeIndex base)
{
profile->computeUpdatedPrediction();
profile->computeUpdatedPrediction(m_inlineStackTop->m_codeBlock);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
if (m_inlineStackTop->m_profiledBlock->numberOfRareCaseProfiles())
......@@ -1783,6 +1783,10 @@ NodeIndex ByteCodeParser::handleGetByOffset(SpeculatedType prediction, NodeIndex
propertyStorage = base;
else
propertyStorage = addToGraph(GetButterfly, base);
// FIXME: It would be far more efficient for load elimination (and safer from
// an OSR standpoint) if GetByOffset also referenced the object we were loading
// from, and if we could load eliminate a GetByOffset even if the butterfly
// had changed. That would be a great success.
NodeIndex getByOffset = addToGraph(GetByOffset, OpInfo(m_graph.m_storageAccessData.size()), OpInfo(prediction), propertyStorage);
StorageAccessData storageAccessData;
......
......@@ -433,6 +433,12 @@ private:
}
return false;
case Arrayify:
case ArrayifyToStructure:
// We could check if the arrayification could affect our structures.
// But that seems like it would take Effort.
return false;
default:
if (m_graph.clobbersWorld(index))
return false;
......@@ -484,6 +490,12 @@ private:
return true;
break;
case Arrayify:
case ArrayifyToStructure:
// We could check if the arrayification could affect our structures.
// But that seems like it would take Effort.
return false;
default:
if (m_graph.clobbersWorld(index))
return false;
......@@ -658,7 +670,6 @@ private:
case AllocatePropertyStorage:
case ReallocatePropertyStorage:
case Arrayify:
// If we can cheaply prove this is a change to our object's storage, we
// can optimize and use its result.
if (node.child1() == child1)
......@@ -684,6 +695,12 @@ private:
}
return NoNode;
case Arrayify:
case ArrayifyToStructure:
// We could check if the arrayification could affect our butterfly.
// But that seems like it would take Effort.
return NoNode;
default:
if (m_graph.clobbersWorld(index))
return NoNode;
......@@ -715,6 +732,12 @@ private:
return true;
break;
case Arrayify:
case ArrayifyToStructure:
// We could check if the arrayification could affect our array.
// But that seems like it would take Effort.
return false;
default:
if (m_graph.clobbersWorld(index))
return false;
......@@ -783,8 +806,6 @@ private:
}
return NoNode;
}
NodeIndex getLocalLoadElimination(VirtualRegister local, NodeIndex& relevantLocalOp, bool careAboutClobbering)
{
......
......@@ -92,17 +92,30 @@ private:
}
case CheckStructure:
case ForwardCheckStructure: {
case ForwardCheckStructure:
case ArrayifyToStructure: {
AbstractValue& value = m_state.forNode(node.child1());
StructureSet set;
if (node.op() == ArrayifyToStructure)
set = node.structure();
else
set = node.structureSet();
if (value.m_currentKnownStructure.isSubsetOf(set)) {
ASSERT(node.refCount() == 1);
node.setOpAndDefaultFlags(Phantom);
eliminated = true;
break;
}
StructureAbstractValue& structureValue = value.m_futurePossibleStructure;
if (structureValue.isSubsetOf(node.structureSet())
if (structureValue.isSubsetOf(set)
&& structureValue.hasSingleton()
&& isCellSpeculation(value.m_type))
node.convertToStructureTransitionWatchpoint(structureValue.singleton());
break;
}
case CheckArray: {
case CheckArray:
case Arrayify: {
if (!node.arrayMode().alreadyChecked(m_state.forNode(node.child1())))
break;
ASSERT(node.refCount() == 1);
......@@ -111,16 +124,6 @@ private:
break;
}
case Arrayify: {
if (!node.arrayMode().alreadyChecked(m_state.forNode(node.child1())))
break;
ASSERT(node.refCount() >= 1);
node.setOpAndDefaultFlags(GetButterfly);
m_graph.deref(nodeIndex);
eliminated = true;
break;
}
default:
break;
}
......
......@@ -88,7 +88,7 @@ private:
nodePtr->codeOrigin.bytecodeIndex);
ArrayMode arrayMode = ArrayMode(Array::SelectUsingPredictions);
if (arrayProfile) {
arrayProfile->computeUpdatedPrediction();
arrayProfile->computeUpdatedPrediction(m_graph.baselineCodeBlockFor(node.codeOrigin));
arrayMode = ArrayMode::fromObserved(arrayProfile, Array::Read, false);
arrayMode = arrayMode.refine(
m_graph[node.child1()].prediction(),
......@@ -382,28 +382,47 @@ private:
if (arrayMode.doesConversion()) {
if (index != NoNode)
m_graph.ref(index);
Node arrayify(Arrayify, codeOrigin, OpInfo(arrayMode.asWord()), array, index);
arrayify.ref(); // Once because it's used as a butterfly.
arrayify.ref(); // And twice because it's must-generate.
NodeIndex arrayifyIndex = m_graph.size();
m_graph.append(arrayify);
m_insertionSet.append(m_indexInBlock, arrayifyIndex);
ASSERT(shouldGenerate);
ASSERT(arrayMode.canCSEStorage());
ASSERT(arrayMode.usesButterfly());
if (!storageCheck(arrayMode))
return NoNode;
return arrayifyIndex;
Structure* structure = 0;
if (arrayMode.isJSArrayWithOriginalStructure()) {
JSGlobalObject* globalObject = m_graph.baselineCodeBlockFor(codeOrigin)->globalObject();
switch (arrayMode.type()) {
case Array::Contiguous:
structure = globalObject->arrayStructure();
if (structure->indexingType() != ArrayWithContiguous)
structure = 0;
break;
case Array::ArrayStorage:
structure = globalObject->arrayStructureWithArrayStorage();
if (structure->indexingType() != ArrayWithArrayStorage)
structure = 0;
break;
default:
break;
}
}
if (structure) {
Node arrayify(ArrayifyToStructure, codeOrigin, OpInfo(structure), OpInfo(arrayMode.asWord()), array, index);
arrayify.ref();
NodeIndex arrayifyIndex = m_graph.size();
m_graph.append(arrayify);
m_insertionSet.append(m_indexInBlock, arrayifyIndex);
} else {
Node arrayify(Arrayify, codeOrigin, OpInfo(arrayMode.asWord()), array, index);
arrayify.ref();
NodeIndex arrayifyIndex = m_graph.size();
m_graph.append(arrayify);