DFG should have some facility for recognizing redundant CheckArrays and Arrayifies
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Oct 2012 06:53:04 +0000 (06:53 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Oct 2012 06:53:04 +0000 (06:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=99287

Reviewed by Mark Hahnenberg.

Adds reasoning about indexing type sets (i.e. ArrayModes) to AbstractValue, which
then enables us to fold away CheckArray's and Arrayify's that are redundant.

* bytecode/ArrayProfile.cpp:
(JSC::arrayModesToString):
(JSC):
* bytecode/ArrayProfile.h:
(JSC):
(JSC::mergeArrayModes):
(JSC::arrayModesAlreadyChecked):
* bytecode/StructureSet.h:
(JSC::StructureSet::arrayModesFromStructures):
(StructureSet):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGAbstractValue.h:
(JSC::DFG::AbstractValue::AbstractValue):
(JSC::DFG::AbstractValue::clear):
(JSC::DFG::AbstractValue::isClear):
(JSC::DFG::AbstractValue::makeTop):
(JSC::DFG::AbstractValue::clobberStructures):
(AbstractValue):
(JSC::DFG::AbstractValue::setMostSpecific):
(JSC::DFG::AbstractValue::set):
(JSC::DFG::AbstractValue::operator==):
(JSC::DFG::AbstractValue::merge):
(JSC::DFG::AbstractValue::filter):
(JSC::DFG::AbstractValue::filterArrayModes):
(JSC::DFG::AbstractValue::validate):
(JSC::DFG::AbstractValue::checkConsistency):
(JSC::DFG::AbstractValue::dump):
(JSC::DFG::AbstractValue::clobberArrayModes):
(JSC::DFG::AbstractValue::clobberArrayModesSlow):
(JSC::DFG::AbstractValue::setFuturePossibleStructure):
(JSC::DFG::AbstractValue::filterFuturePossibleStructure):
* dfg/DFGArrayMode.cpp:
(JSC::DFG::modeAlreadyChecked):
* dfg/DFGArrayMode.h:
(JSC::DFG::arrayModesFor):
(DFG):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::arrayify):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@131982 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/ArrayProfile.cpp
Source/JavaScriptCore/bytecode/ArrayProfile.h
Source/JavaScriptCore/bytecode/StructureSet.h
Source/JavaScriptCore/dfg/DFGAbstractState.cpp
Source/JavaScriptCore/dfg/DFGAbstractValue.h
Source/JavaScriptCore/dfg/DFGArrayMode.cpp
Source/JavaScriptCore/dfg/DFGArrayMode.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp

index 9fc5c7d..2ec24d5 100644 (file)
@@ -1,5 +1,57 @@
 2012-10-19  Filip Pizlo  <fpizlo@apple.com>
 
+        DFG should have some facility for recognizing redundant CheckArrays and Arrayifies
+        https://bugs.webkit.org/show_bug.cgi?id=99287
+
+        Reviewed by Mark Hahnenberg.
+
+        Adds reasoning about indexing type sets (i.e. ArrayModes) to AbstractValue, which
+        then enables us to fold away CheckArray's and Arrayify's that are redundant.
+
+        * bytecode/ArrayProfile.cpp:
+        (JSC::arrayModesToString):
+        (JSC):
+        * bytecode/ArrayProfile.h:
+        (JSC):
+        (JSC::mergeArrayModes):
+        (JSC::arrayModesAlreadyChecked):
+        * bytecode/StructureSet.h:
+        (JSC::StructureSet::arrayModesFromStructures):
+        (StructureSet):
+        * dfg/DFGAbstractState.cpp:
+        (JSC::DFG::AbstractState::execute):
+        * dfg/DFGAbstractValue.h:
+        (JSC::DFG::AbstractValue::AbstractValue):
+        (JSC::DFG::AbstractValue::clear):
+        (JSC::DFG::AbstractValue::isClear):
+        (JSC::DFG::AbstractValue::makeTop):
+        (JSC::DFG::AbstractValue::clobberStructures):
+        (AbstractValue):
+        (JSC::DFG::AbstractValue::setMostSpecific):
+        (JSC::DFG::AbstractValue::set):
+        (JSC::DFG::AbstractValue::operator==):
+        (JSC::DFG::AbstractValue::merge):
+        (JSC::DFG::AbstractValue::filter):
+        (JSC::DFG::AbstractValue::filterArrayModes):
+        (JSC::DFG::AbstractValue::validate):
+        (JSC::DFG::AbstractValue::checkConsistency):
+        (JSC::DFG::AbstractValue::dump):
+        (JSC::DFG::AbstractValue::clobberArrayModes):
+        (JSC::DFG::AbstractValue::clobberArrayModesSlow):
+        (JSC::DFG::AbstractValue::setFuturePossibleStructure):
+        (JSC::DFG::AbstractValue::filterFuturePossibleStructure):
+        * dfg/DFGArrayMode.cpp:
+        (JSC::DFG::modeAlreadyChecked):
+        * dfg/DFGArrayMode.h:
+        (JSC::DFG::arrayModesFor):
+        (DFG):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::arrayify):
+
+2012-10-19  Filip Pizlo  <fpizlo@apple.com>
+
         Baseline JIT should not inline array allocations, to make them easier to instrument
         https://bugs.webkit.org/show_bug.cgi?id=99905
 
index 3ba974d..de7f678 100644 (file)
 #include "config.h"
 #include "ArrayProfile.h"
 
+#include <wtf/StringExtras.h>
+
 namespace JSC {
 
+const char* arrayModesToString(ArrayModes arrayModes)
+{
+    if (!arrayModes)
+        return "0:<empty>";
+    
+    if (arrayModes == ALL_ARRAY_MODES)
+        return "TOP";
+
+    bool isNonArray = !!(arrayModes & NonArray);
+    bool isNonArrayWithContiguous = !!(arrayModes & NonArrayWithContiguous);
+    bool isNonArrayWithArrayStorage = !!(arrayModes & NonArrayWithArrayStorage);
+    bool isNonArrayWithSlowPutArrayStorage = !!(arrayModes & NonArrayWithSlowPutArrayStorage);
+    bool isArray = !!(arrayModes & ArrayClass);
+    bool isArrayWithContiguous = !!(arrayModes & ArrayWithContiguous);
+    bool isArrayWithArrayStorage = !!(arrayModes & ArrayWithArrayStorage);
+    bool isArrayWithSlowPutArrayStorage = !!(arrayModes & ArrayWithSlowPutArrayStorage);
+    
+    static char result[256];
+    snprintf(
+        result, sizeof(result),
+        "%u:%s%s%s%s%s%s%s%s",
+        arrayModes,
+        isNonArray ? "NonArray" : "",
+        isNonArrayWithContiguous ? "NonArrayWithContiguous" : "",
+        isNonArrayWithArrayStorage ? " NonArrayWithArrayStorage" : "",
+        isNonArrayWithSlowPutArrayStorage ? "NonArrayWithSlowPutArrayStorage" : "",
+        isArray ? "ArrayClass" : "",
+        isArrayWithContiguous ? "ArrayWithContiguous" : "",
+        isArrayWithArrayStorage ? " ArrayWithArrayStorage" : "",
+        isArrayWithSlowPutArrayStorage ? "ArrayWithSlowPutArrayStorage" : "");
+    
+    return result;
+}
+
 void ArrayProfile::computeUpdatedPrediction(OperationInProgress operation)
 {
     if (m_lastSeenStructure) {
index 3b462ea..ffc1362 100644 (file)
@@ -40,13 +40,44 @@ class LLIntOffsetsExtractor;
 typedef unsigned ArrayModes;
 
 #define asArrayModes(type) \
-    (1 << static_cast<unsigned>(type))
+    (static_cast<unsigned>(1) << static_cast<unsigned>(type))
+
+#define ALL_NON_ARRAY_ARRAY_MODES                       \
+    (asArrayModes(NonArray)                             \
+     | asArrayModes(NonArrayWithContiguous)             \
+     | asArrayModes(NonArrayWithArrayStorage)           \
+     | asArrayModes(NonArrayWithSlowPutArrayStorage))
+
+#define ALL_ARRAY_ARRAY_MODES                           \
+    (asArrayModes(ArrayClass)                           \
+     | asArrayModes(ArrayWithContiguous)                \
+     | asArrayModes(ArrayWithArrayStorage)              \
+     | asArrayModes(ArrayWithSlowPutArrayStorage))
+
+#define ALL_ARRAY_MODES (ALL_NON_ARRAY_ARRAY_MODES | ALL_ARRAY_ARRAY_MODES)
 
 inline ArrayModes arrayModeFromStructure(Structure* structure)
 {
     return asArrayModes(structure->indexingType());
 }
 
+const char* arrayModesToString(ArrayModes);
+
+inline bool mergeArrayModes(ArrayModes& left, ArrayModes right)
+{
+    ArrayModes newModes = left | right;
+    if (newModes == left)
+        return false;
+    left = newModes;
+    return true;
+}
+
+// Checks if proven is a subset of expected.
+inline bool arrayModesAlreadyChecked(ArrayModes proven, ArrayModes expected)
+{
+    return (expected | proven) == expected;
+}
+
 class ArrayProfile {
 public:
     ArrayProfile()
index ebde977..c95d304 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef StructureSet_h
 #define StructureSet_h
 
+#include "ArrayProfile.h"
 #include "SpeculatedType.h"
 #include "Structure.h"
 #include <stdio.h>
@@ -137,6 +138,16 @@ public:
         return result;
     }
     
+    ArrayModes arrayModesFromStructures() const
+    {
+        ArrayModes result = 0;
+        
+        for (size_t i = 0; i < m_structures.size(); ++i)
+            mergeArrayModes(result, asArrayModes(m_structures[i]->indexingType()));
+        
+        return result;
+    }
+    
     bool operator==(const StructureSet& other) const
     {
         if (m_structures.size() != other.m_structures.size())
index f34582b..928788b 100644 (file)
@@ -1420,9 +1420,15 @@ bool AbstractState::execute(unsigned indexInBlock)
             ASSERT_NOT_REACHED();
             break;
         }
+        forNode(node.child1()).filterArrayModes(arrayModesFor(node.arrayMode()));
         break;
     }
     case Arrayify: {
+        if (modeAlreadyChecked(forNode(node.child1()), node.arrayMode())) {
+            m_foundConstants = true;
+            node.setCanExit(false);
+            break;
+        }
         switch (node.arrayMode()) {
         case ALL_EFFECTFUL_MODES:
             node.setCanExit(true);
@@ -1431,9 +1437,10 @@ bool AbstractState::execute(unsigned indexInBlock)
                 forNode(node.child2()).filter(SpecInt32);
             forNode(nodeIndex).clear();
             clobberStructures(indexInBlock);
+            forNode(node.child1()).filterArrayModes(arrayModesFor(node.arrayMode()));
             break;
         default:
-            ASSERT_NOT_REACHED();
+            CRASH();
             break;
         }
         break;
index ff1c6d2..5382cd3 100644 (file)
@@ -30,6 +30,7 @@
 
 #if ENABLE(DFG_JIT)
 
+#include "ArrayProfile.h"
 #include "DFGStructureAbstractValue.h"
 #include "JSCell.h"
 #include "SpeculatedType.h"
@@ -40,12 +41,14 @@ namespace JSC { namespace DFG {
 struct AbstractValue {
     AbstractValue()
         : m_type(SpecNone)
+        , m_arrayModes(0)
     {
     }
     
     void clear()
     {
         m_type = SpecNone;
+        m_arrayModes = 0;
         m_currentKnownStructure.clear();
         m_futurePossibleStructure.clear();
         m_value = JSValue();
@@ -54,7 +57,7 @@ struct AbstractValue {
     
     bool isClear() const
     {
-        bool result = m_type == SpecNone && m_currentKnownStructure.isClear() && m_futurePossibleStructure.isClear();
+        bool result = m_type == SpecNone && !m_arrayModes && m_currentKnownStructure.isClear() && m_futurePossibleStructure.isClear();
         if (result)
             ASSERT(!m_value);
         return result;
@@ -63,6 +66,7 @@ struct AbstractValue {
     void makeTop()
     {
         m_type = SpecTop;
+        m_arrayModes = ALL_ARRAY_MODES;
         m_currentKnownStructure.makeTop();
         m_futurePossibleStructure.makeTop();
         m_value = JSValue();
@@ -71,13 +75,16 @@ struct AbstractValue {
     
     void clobberStructures()
     {
-        if (m_type & SpecCell)
+        if (m_type & SpecCell) {
             m_currentKnownStructure.makeTop();
-        else
+            clobberArrayModes();
+        } else {
             ASSERT(m_currentKnownStructure.isClear());
+            ASSERT(!m_arrayModes);
+        }
         checkConsistency();
     }
-    
+        
     void clobberValue()
     {
         m_value = JSValue();
@@ -105,29 +112,17 @@ struct AbstractValue {
         return result;
     }
     
-    void setFuturePossibleStructure(Structure* structure)
-    {
-        if (structure->transitionWatchpointSetIsStillValid())
-            m_futurePossibleStructure = structure;
-        else
-            m_futurePossibleStructure.makeTop();
-    }
-    
-    void filterFuturePossibleStructure(Structure* structure)
-    {
-        if (structure->transitionWatchpointSetIsStillValid())
-            m_futurePossibleStructure.filter(StructureAbstractValue(structure));
-    }
-    
     void setMostSpecific(JSValue value)
     {
         if (!!value && value.isCell()) {
             Structure* structure = value.asCell()->structure();
             m_currentKnownStructure = structure;
             setFuturePossibleStructure(structure);
+            m_arrayModes = asArrayModes(structure->indexingType());
         } else {
             m_currentKnownStructure.clear();
             m_futurePossibleStructure.clear();
+            m_arrayModes = 0;
         }
         
         m_type = speculationFromValue(value);
@@ -140,10 +135,14 @@ struct AbstractValue {
     {
         if (!!value && value.isCell()) {
             m_currentKnownStructure.makeTop();
-            setFuturePossibleStructure(value.asCell()->structure());
+            Structure* structure = value.asCell()->structure();
+            setFuturePossibleStructure(structure);
+            m_arrayModes = asArrayModes(structure->indexingType());
+            clobberArrayModes();
         } else {
             m_currentKnownStructure.clear();
             m_futurePossibleStructure.clear();
+            m_arrayModes = 0;
         }
         
         m_type = speculationFromValue(value);
@@ -156,6 +155,7 @@ struct AbstractValue {
     {
         m_currentKnownStructure = structure;
         setFuturePossibleStructure(structure);
+        m_arrayModes = asArrayModes(structure->indexingType());
         m_type = speculationFromStructure(structure);
         m_value = JSValue();
         
@@ -167,9 +167,11 @@ struct AbstractValue {
         if (type & SpecCell) {
             m_currentKnownStructure.makeTop();
             m_futurePossibleStructure.makeTop();
+            m_arrayModes = ALL_ARRAY_MODES;
         } else {
             m_currentKnownStructure.clear();
             m_futurePossibleStructure.clear();
+            m_arrayModes = 0;
         }
         m_type = type;
         m_value = JSValue();
@@ -179,6 +181,7 @@ struct AbstractValue {
     bool operator==(const AbstractValue& other) const
     {
         return m_type == other.m_type
+            && m_arrayModes == other.m_arrayModes
             && m_currentKnownStructure == other.m_currentKnownStructure
             && m_futurePossibleStructure == other.m_futurePossibleStructure
             && m_value == other.m_value;
@@ -199,6 +202,7 @@ struct AbstractValue {
             result = !other.isClear();
         } else {
             result |= mergeSpeculation(m_type, other.m_type);
+            result |= mergeArrayModes(m_arrayModes, other.m_arrayModes);
             result |= m_currentKnownStructure.addAll(other.m_currentKnownStructure);
             result |= m_futurePossibleStructure.addAll(other.m_futurePossibleStructure);
             if (m_value != other.m_value) {
@@ -218,6 +222,7 @@ struct AbstractValue {
         if (type & SpecCell) {
             m_currentKnownStructure.makeTop();
             m_futurePossibleStructure.makeTop();
+            m_arrayModes = ALL_ARRAY_MODES;
         }
         m_value = JSValue();
 
@@ -227,6 +232,7 @@ struct AbstractValue {
     void filter(const StructureSet& other)
     {
         m_type &= other.speculationFromStructures();
+        m_arrayModes &= other.arrayModesFromStructures();
         m_currentKnownStructure.filter(other);
         if (m_currentKnownStructure.isClear())
             m_futurePossibleStructure.clear();
@@ -241,11 +247,24 @@ struct AbstractValue {
         m_currentKnownStructure.filter(m_type);
         m_futurePossibleStructure.filter(m_type);
         
+        filterArrayModesByType();
         filterValueByType();
         
         checkConsistency();
     }
     
+    void filterArrayModes(ArrayModes arrayModes)
+    {
+        ASSERT(arrayModes);
+        
+        m_type &= SpecCell;
+        m_arrayModes &= arrayModes;
+        
+        // I could do more fancy filtering here. But it probably won't make any difference.
+        
+        checkConsistency();
+    }
+    
     void filter(SpeculatedType type)
     {
         if (type == SpecTop)
@@ -258,31 +277,13 @@ struct AbstractValue {
         // the new type (None) rather than the one passed (Array).
         m_currentKnownStructure.filter(m_type);
         m_futurePossibleStructure.filter(m_type);
-
+        
+        filterArrayModesByType();
         filterValueByType();
         
         checkConsistency();
     }
     
-    // We could go further, and ensure that if the futurePossibleStructure contravenes
-    // the value, then we could clear both of those things. But that's unlikely to help
-    // in any realistic scenario, so we don't do it. Simpler is better.
-    void filterValueByType()
-    {
-        if (!!m_type) {
-            // The type is still non-empty. This implies that regardless of what filtering
-            // was done, we either didn't have a value to begin with, or that value is still
-            // valid.
-            ASSERT(!m_value || validateType(m_value));
-            return;
-        }
-        
-        // The type has been rendered empty. That means that the value must now be invalid,
-        // as well.
-        ASSERT(!m_value || !validateType(m_value));
-        m_value = JSValue();
-    }
-    
     bool validateType(JSValue value) const
     {
         if (isTop())
@@ -319,7 +320,8 @@ struct AbstractValue {
             ASSERT(m_type & SpecCell);
             Structure* structure = value.asCell()->structure();
             return m_currentKnownStructure.contains(structure)
-                && m_futurePossibleStructure.contains(structure);
+                && m_futurePossibleStructure.contains(structure)
+                && (m_arrayModes & asArrayModes(structure->indexingType()));
         }
         
         return true;
@@ -330,6 +332,7 @@ struct AbstractValue {
         if (!(m_type & SpecCell)) {
             ASSERT(m_currentKnownStructure.isClear());
             ASSERT(m_futurePossibleStructure.isClear());
+            ASSERT(!m_arrayModes);
         }
         
         if (isClear())
@@ -346,7 +349,7 @@ struct AbstractValue {
     
     void dump(FILE* out) const
     {
-        fprintf(out, "(%s, ", speculationToString(m_type));
+        fprintf(out, "(%s, %s, ", speculationToString(m_type), arrayModesToString(m_arrayModes));
         m_currentKnownStructure.dump(out);
         dataLog(", ");
         m_futurePossibleStructure.dump(out);
@@ -437,6 +440,13 @@ struct AbstractValue {
     // unified with the set of all objects with structure 0x12345.
     SpeculatedType m_type;
     
+    // This is a proven constraint on the possible indexing types that this value
+    // can have right now. It also implicitly constraints the set of structures
+    // that the value may have right now, since a structure has an immutable
+    // indexing type. This is subject to change upon reassignment, or any side
+    // effect that makes non-obvious changes to the heap.
+    ArrayModes m_arrayModes;
+    
     // This is a proven constraint on the possible values that this value can
     // have now or any time in the future, unless it is reassigned. Note that this
     // implies nothing about the structure. Oddly, JSValue() (i.e. the empty value)
@@ -444,6 +454,75 @@ struct AbstractValue {
     // BOTTOM then JSValue() means BOTTOM; if m_type is not BOTTOM then JSValue()
     // means TOP.
     JSValue m_value;
+
+private:
+    void clobberArrayModes()
+    {
+        if (m_arrayModes == ALL_ARRAY_MODES)
+            return;
+        
+        if (LIKELY(m_arrayModes & asArrayModes(NonArray)))
+            m_arrayModes = ALL_ARRAY_MODES;
+        else
+            clobberArrayModesSlow();
+    }
+    
+    void clobberArrayModesSlow()
+    {
+        if (m_arrayModes & asArrayModes(ArrayClass))
+            m_arrayModes = ALL_ARRAY_MODES;
+        else if (m_arrayModes & asArrayModes(NonArrayWithContiguous))
+            m_arrayModes |= asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage);
+        else if (m_arrayModes & asArrayModes(ArrayWithContiguous))
+            m_arrayModes |= asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage);
+        else if (m_arrayModes & asArrayModes(NonArrayWithArrayStorage))
+            m_arrayModes |= asArrayModes(NonArrayWithSlowPutArrayStorage);
+        else if (m_arrayModes & asArrayModes(ArrayWithArrayStorage))
+            m_arrayModes |= asArrayModes(ArrayWithArrayStorage);
+    }
+
+    void setFuturePossibleStructure(Structure* structure)
+    {
+        if (structure->transitionWatchpointSetIsStillValid())
+            m_futurePossibleStructure = structure;
+        else
+            m_futurePossibleStructure.makeTop();
+    }
+    
+    void filterFuturePossibleStructure(Structure* structure)
+    {
+        if (structure->transitionWatchpointSetIsStillValid())
+            m_futurePossibleStructure.filter(StructureAbstractValue(structure));
+    }
+
+    // We could go further, and ensure that if the futurePossibleStructure contravenes
+    // the value, then we could clear both of those things. But that's unlikely to help
+    // in any realistic scenario, so we don't do it. Simpler is better.
+    void filterValueByType()
+    {
+        if (!!m_type) {
+            // The type is still non-empty. This implies that regardless of what filtering
+            // was done, we either didn't have a value to begin with, or that value is still
+            // valid.
+            ASSERT(!m_value || validateType(m_value));
+            return;
+        }
+        
+        // The type has been rendered empty. That means that the value must now be invalid,
+        // as well.
+        ASSERT(!m_value || !validateType(m_value));
+        m_value = JSValue();
+    }
+    
+    void filterArrayModesByType()
+    {
+        if (!(m_type & SpecCell))
+            m_arrayModes = 0;
+        else if (!(m_type & ~SpecArray))
+            m_arrayModes &= ALL_ARRAY_ARRAY_MODES;
+        else if (!(m_type & SpecArray))
+            m_arrayModes &= ALL_NON_ARRAY_ARRAY_MODES;
+    }
 };
 
 } } // namespace JSC::DFG
index 3985d76..623e9d7 100644 (file)
@@ -167,12 +167,17 @@ bool modeAlreadyChecked(AbstractValue& value, Array::Mode arrayMode)
     case Array::PossiblyArrayWithContiguous:
     case Array::PossiblyArrayWithContiguousToTail:
     case Array::PossiblyArrayWithContiguousOutOfBounds:
+    case Array::ToContiguous:
+        if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous)))
+            return true;
         return value.m_currentKnownStructure.hasSingleton()
             && hasContiguous(value.m_currentKnownStructure.singleton()->indexingType());
         
     case Array::ArrayWithContiguous:
     case Array::ArrayWithContiguousToTail:
     case Array::ArrayWithContiguousOutOfBounds:
+        if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithContiguous)))
+            return true;
         return value.m_currentKnownStructure.hasSingleton()
             && hasContiguous(value.m_currentKnownStructure.singleton()->indexingType())
             && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray);
@@ -183,29 +188,38 @@ bool modeAlreadyChecked(AbstractValue& value, Array::Mode arrayMode)
     case Array::PossiblyArrayWithArrayStorage:
     case Array::PossiblyArrayWithArrayStorageToHole:
     case Array::PossiblyArrayWithArrayStorageOutOfBounds:
+    case Array::ToArrayStorage:
+    case Array::PossiblyArrayToArrayStorage:
+        if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage)))
+            return true;
         return value.m_currentKnownStructure.hasSingleton()
             && hasFastArrayStorage(value.m_currentKnownStructure.singleton()->indexingType());
         
     case Array::SlowPutArrayStorage:
     case Array::PossiblyArrayWithSlowPutArrayStorage:
+    case Array::ToSlowPutArrayStorage:
+        if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage)))
+            return true;
         return value.m_currentKnownStructure.hasSingleton()
             && hasArrayStorage(value.m_currentKnownStructure.singleton()->indexingType());
         
     case Array::ArrayWithArrayStorage:
     case Array::ArrayWithArrayStorageToHole:
     case Array::ArrayWithArrayStorageOutOfBounds:
+    case Array::ArrayToArrayStorage:
+        if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage)))
+            return true;
         return value.m_currentKnownStructure.hasSingleton()
             && hasFastArrayStorage(value.m_currentKnownStructure.singleton()->indexingType())
             && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray);
         
     case Array::ArrayWithSlowPutArrayStorage:
+        if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage)))
+            return true;
         return value.m_currentKnownStructure.hasSingleton()
             && hasArrayStorage(value.m_currentKnownStructure.singleton()->indexingType())
             && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray);
         
-    case ALL_EFFECTFUL_MODES:
-        return false;
-        
     case Array::Arguments:
         return isArgumentsSpeculation(value.m_type);
         
index a666bb8..f7ac927 100644 (file)
@@ -349,6 +349,51 @@ inline bool isEffectful(Array::Mode mode)
     }
 }
 
+// This returns the set of array modes that will pass filtering of a CheckArray or
+// Arrayify with the given mode.
+inline ArrayModes arrayModesFor(Array::Mode arrayMode)
+{
+    switch (arrayMode) {
+    case Array::Generic:
+        return ALL_ARRAY_MODES;
+    case Array::Contiguous:
+    case Array::ContiguousToTail:
+    case Array::ContiguousOutOfBounds:
+    case Array::ToContiguous:
+        return asArrayModes(NonArrayWithContiguous);
+    case Array::PossiblyArrayWithContiguous:
+    case Array::PossiblyArrayWithContiguousToTail:
+    case Array::PossiblyArrayWithContiguousOutOfBounds:
+        return asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous);
+    case ARRAY_WITH_CONTIGUOUS_MODES:
+        return asArrayModes(ArrayWithContiguous);
+    case Array::ArrayStorage:
+    case Array::ArrayStorageToHole:
+    case Array::ArrayStorageOutOfBounds:
+    case Array::ToArrayStorage:
+        return asArrayModes(NonArrayWithArrayStorage);
+    case Array::ToSlowPutArrayStorage:
+    case Array::SlowPutArrayStorage:
+        return asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage);
+    case Array::PossiblyArrayWithArrayStorage:
+    case Array::PossiblyArrayWithArrayStorageToHole:
+    case Array::PossiblyArrayWithArrayStorageOutOfBounds:
+    case Array::PossiblyArrayToArrayStorage:
+        return asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage);
+    case Array::PossiblyArrayWithSlowPutArrayStorage:
+        return asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage);
+    case Array::ArrayWithArrayStorage:
+    case Array::ArrayWithArrayStorageToHole:
+    case Array::ArrayWithArrayStorageOutOfBounds:
+    case Array::ArrayToArrayStorage:
+        return asArrayModes(ArrayWithArrayStorage);
+    case Array::ArrayWithSlowPutArrayStorage:
+        return asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage);
+    default:
+        return asArrayModes(NonArray);
+    }
+}
+
 } } // namespace JSC::DFG
 
 #endif // ENABLE(DFG_JIT)
index 8a261ad..25915cf 100644 (file)
@@ -102,7 +102,8 @@ private:
                 break;
             }
                 
-            case CheckArray: {
+            case CheckArray:
+            case Arrayify: {
                 if (!modeAlreadyChecked(m_state.forNode(node.child1()), node.arrayMode()))
                     break;
                 ASSERT(node.refCount() == 1);
index fdcb63e..a9b91d0 100644 (file)
@@ -579,10 +579,17 @@ void SpeculativeJIT::arrayify(Node& node, GPRReg baseReg, GPRReg propertyReg)
 void SpeculativeJIT::arrayify(Node& node)
 {
     ASSERT(modeIsSpecific(node.arrayMode()));
-    ASSERT(!modeAlreadyChecked(m_state.forNode(node.child1()), node.arrayMode()));
     
     SpeculateCellOperand base(this, node.child1());
     
+    if (modeAlreadyChecked(m_state.forNode(node.child1()), node.arrayMode())) {
+        GPRTemporary temp(this);
+        m_jit.loadPtr(
+            MacroAssembler::Address(base.gpr(), JSObject::butterflyOffset()), temp.gpr());
+        storageResult(temp.gpr(), m_compileIndex);
+        return;
+    }
+    
     if (!node.child2()) {
         arrayify(node, base.gpr(), InvalidGPRReg);
         return;