]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - dfg/DFGArrayMode.cpp
JavaScriptCore-1218.tar.gz
[apple/javascriptcore.git] / dfg / DFGArrayMode.cpp
diff --git a/dfg/DFGArrayMode.cpp b/dfg/DFGArrayMode.cpp
new file mode 100644 (file)
index 0000000..ede2ffc
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "DFGArrayMode.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGAbstractValue.h"
+#include "DFGGraph.h"
+#include "Operations.h"
+
+namespace JSC { namespace DFG {
+
+ArrayMode ArrayMode::fromObserved(ArrayProfile* profile, Array::Action action, bool makeSafe)
+{
+    ArrayModes observed = profile->observedArrayModes();
+    switch (observed) {
+    case 0:
+        return ArrayMode(Array::Unprofiled);
+    case asArrayModes(NonArray):
+        if (action == Array::Write && !profile->mayInterceptIndexedAccesses())
+            return ArrayMode(Array::Undecided, Array::NonArray, Array::OutOfBounds, Array::Convert);
+        return ArrayMode(Array::SelectUsingPredictions);
+
+    case asArrayModes(ArrayWithUndecided):
+        if (action == Array::Write)
+            return ArrayMode(Array::Undecided, Array::Array, Array::OutOfBounds, Array::Convert);
+        return ArrayMode(Array::Generic);
+        
+    case asArrayModes(NonArray) | asArrayModes(ArrayWithUndecided):
+        if (action == Array::Write && !profile->mayInterceptIndexedAccesses())
+            return ArrayMode(Array::Undecided, Array::PossiblyArray, Array::OutOfBounds, Array::Convert);
+        return ArrayMode(Array::SelectUsingPredictions);
+
+    case asArrayModes(NonArrayWithInt32):
+        return ArrayMode(Array::Int32, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe);
+    case asArrayModes(ArrayWithInt32):
+        return ArrayMode(Array::Int32, Array::Array, Array::AsIs).withProfile(profile, makeSafe);
+    case asArrayModes(NonArrayWithInt32) | asArrayModes(ArrayWithInt32):
+        return ArrayMode(Array::Int32, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe);
+
+    case asArrayModes(NonArrayWithDouble):
+        return ArrayMode(Array::Double, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe);
+    case asArrayModes(ArrayWithDouble):
+        return ArrayMode(Array::Double, Array::Array, Array::AsIs).withProfile(profile, makeSafe);
+    case asArrayModes(NonArrayWithDouble) | asArrayModes(ArrayWithDouble):
+        return ArrayMode(Array::Double, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe);
+
+    case asArrayModes(NonArrayWithContiguous):
+        return ArrayMode(Array::Contiguous, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe);
+    case asArrayModes(ArrayWithContiguous):
+        return ArrayMode(Array::Contiguous, Array::Array, Array::AsIs).withProfile(profile, makeSafe);
+    case asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous):
+        return ArrayMode(Array::Contiguous, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe);
+
+    case asArrayModes(NonArrayWithArrayStorage):
+        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).withProfile(profile, makeSafe);
+    case asArrayModes(ArrayWithArrayStorage):
+        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).withProfile(profile, makeSafe);
+    case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage):
+        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).withProfile(profile, makeSafe);
+
+    default:
+        if ((observed & asArrayModes(NonArray)) && profile->mayInterceptIndexedAccesses())
+            return ArrayMode(Array::SelectUsingPredictions);
+        
+        Array::Type type;
+        Array::Class arrayClass;
+        
+        if (shouldUseSlowPutArrayStorage(observed))
+            type = Array::SlowPutArrayStorage;
+        else if (shouldUseFastArrayStorage(observed))
+            type = Array::ArrayStorage;
+        else if (shouldUseContiguous(observed))
+            type = Array::Contiguous;
+        else if (shouldUseDouble(observed))
+            type = Array::Double;
+        else if (shouldUseInt32(observed))
+            type = Array::Int32;
+        else
+            type = Array::Undecided;
+        
+        if (hasSeenArray(observed) && hasSeenNonArray(observed))
+            arrayClass = Array::PossiblyArray;
+        else if (hasSeenArray(observed))
+            arrayClass = Array::Array;
+        else if (hasSeenNonArray(observed))
+            arrayClass = Array::NonArray;
+        else
+            arrayClass = Array::PossiblyArray;
+        
+        return ArrayMode(type, arrayClass, Array::Convert).withProfile(profile, makeSafe);
+    }
+}
+
+ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index, SpeculatedType value, NodeFlags flags) const
+{
+    if (!base || !index) {
+        // It can be that we had a legitimate arrayMode but no incoming predictions. That'll
+        // happen if we inlined code based on, say, a global variable watchpoint, but later
+        // realized that the callsite could not have possibly executed. It may be worthwhile
+        // to fix that, but for now I'm leaving it as-is.
+        return ArrayMode(Array::ForceExit);
+    }
+    
+    if (!isInt32Speculation(index))
+        return ArrayMode(Array::Generic);
+    
+    // Note: our profiling currently doesn't give us good information in case we have
+    // an unlikely control flow path that sets the base to a non-cell value. Value
+    // profiling and prediction propagation will probably tell us that the value is
+    // either a cell or not, but that doesn't tell us which is more likely: that this
+    // is an array access on a cell (what we want and can optimize) or that the user is
+    // doing a crazy by-val access on a primitive (we can't easily optimize this and
+    // don't want to). So, for now, we assume that if the base is not a cell according
+    // to value profiling, but the array profile tells us something else, then we
+    // should just trust the array profile.
+    
+    switch (type()) {
+    case Array::Unprofiled:
+        return ArrayMode(Array::ForceExit);
+        
+    case Array::Undecided:
+        if (!value)
+            return withType(Array::ForceExit);
+        if (isInt32Speculation(value))
+            return withTypeAndConversion(Array::Int32, Array::Convert);
+        if (isNumberSpeculation(value))
+            return withTypeAndConversion(Array::Double, Array::Convert);
+        return withTypeAndConversion(Array::Contiguous, Array::Convert);
+        
+    case Array::Int32:
+        if (!value || isInt32Speculation(value))
+            return *this;
+        if (isNumberSpeculation(value))
+            return withTypeAndConversion(Array::Double, Array::Convert);
+        return withTypeAndConversion(Array::Contiguous, Array::Convert);
+        
+    case Array::Double:
+        if (flags & NodeUsedAsInt)
+            return withTypeAndConversion(Array::Contiguous, Array::RageConvert);
+        if (!value || isNumberSpeculation(value))
+            return *this;
+        return withTypeAndConversion(Array::Contiguous, Array::Convert);
+        
+    case Array::Contiguous:
+        if (doesConversion() && (flags & NodeUsedAsInt))
+            return withConversion(Array::RageConvert);
+        return *this;
+        
+    case Array::SelectUsingPredictions:
+        base &= ~SpecOther;
+        
+        if (isStringSpeculation(base))
+            return ArrayMode(Array::String);
+        
+        if (isArgumentsSpeculation(base))
+            return ArrayMode(Array::Arguments);
+        
+        if (isInt8ArraySpeculation(base))
+            return ArrayMode(Array::Int8Array);
+        
+        if (isInt16ArraySpeculation(base))
+            return ArrayMode(Array::Int16Array);
+        
+        if (isInt32ArraySpeculation(base))
+            return ArrayMode(Array::Int32Array);
+        
+        if (isUint8ArraySpeculation(base))
+            return ArrayMode(Array::Uint8Array);
+        
+        if (isUint8ClampedArraySpeculation(base))
+            return ArrayMode(Array::Uint8ClampedArray);
+        
+        if (isUint16ArraySpeculation(base))
+            return ArrayMode(Array::Uint16Array);
+        
+        if (isUint32ArraySpeculation(base))
+            return ArrayMode(Array::Uint32Array);
+        
+        if (isFloat32ArraySpeculation(base))
+            return ArrayMode(Array::Float32Array);
+        
+        if (isFloat64ArraySpeculation(base))
+            return ArrayMode(Array::Float64Array);
+
+        return ArrayMode(Array::Generic);
+
+    default:
+        return *this;
+    }
+}
+
+Structure* ArrayMode::originalArrayStructure(Graph& graph, const CodeOrigin& codeOrigin) const
+{
+    if (!isJSArrayWithOriginalStructure())
+        return 0;
+    
+    JSGlobalObject* globalObject = graph.globalObjectFor(codeOrigin);
+    
+    switch (type()) {
+    case Array::Int32:
+        return globalObject->originalArrayStructureForIndexingType(ArrayWithInt32);
+    case Array::Double:
+        return globalObject->originalArrayStructureForIndexingType(ArrayWithDouble);
+    case Array::Contiguous:
+        return globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous);
+    case Array::ArrayStorage:
+        return globalObject->originalArrayStructureForIndexingType(ArrayWithArrayStorage);
+    default:
+        CRASH();
+        return 0;
+    }
+}
+
+Structure* ArrayMode::originalArrayStructure(Graph& graph, Node* node) const
+{
+    return originalArrayStructure(graph, node->codeOrigin);
+}
+
+bool ArrayMode::alreadyChecked(Graph& graph, Node* node, AbstractValue& value, IndexingType shape) const
+{
+    switch (arrayClass()) {
+    case Array::OriginalArray:
+        return value.m_currentKnownStructure.hasSingleton()
+            && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape
+            && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray)
+            && graph.globalObjectFor(node->codeOrigin)->isOriginalArrayStructure(value.m_currentKnownStructure.singleton());
+        
+    case Array::Array:
+        if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(shape | IsArray)))
+            return true;
+        return value.m_currentKnownStructure.hasSingleton()
+            && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape
+            && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray);
+        
+    default:
+        if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(shape) | asArrayModes(shape | IsArray)))
+            return true;
+        return value.m_currentKnownStructure.hasSingleton()
+            && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape;
+    }
+}
+
+bool ArrayMode::alreadyChecked(Graph& graph, Node* node, AbstractValue& value) const
+{
+    switch (type()) {
+    case Array::Generic:
+        return true;
+        
+    case Array::ForceExit:
+        return false;
+        
+    case Array::String:
+        return speculationChecked(value.m_type, SpecString);
+        
+    case Array::Int32:
+        return alreadyChecked(graph, node, value, Int32Shape);
+        
+    case Array::Double:
+        return alreadyChecked(graph, node, value, DoubleShape);
+        
+    case Array::Contiguous:
+        return alreadyChecked(graph, node, value, ContiguousShape);
+        
+    case Array::ArrayStorage:
+        return alreadyChecked(graph, node, value, ArrayStorageShape);
+        
+    case Array::SlowPutArrayStorage:
+        switch (arrayClass()) {
+        case Array::OriginalArray:
+            CRASH();
+            return false;
+        
+        case Array::Array:
+            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);
+        
+        default:
+            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::Arguments:
+        return speculationChecked(value.m_type, SpecArguments);
+        
+    case Array::Int8Array:
+        return speculationChecked(value.m_type, SpecInt8Array);
+        
+    case Array::Int16Array:
+        return speculationChecked(value.m_type, SpecInt16Array);
+        
+    case Array::Int32Array:
+        return speculationChecked(value.m_type, SpecInt32Array);
+        
+    case Array::Uint8Array:
+        return speculationChecked(value.m_type, SpecUint8Array);
+        
+    case Array::Uint8ClampedArray:
+        return speculationChecked(value.m_type, SpecUint8ClampedArray);
+        
+    case Array::Uint16Array:
+        return speculationChecked(value.m_type, SpecUint16Array);
+        
+    case Array::Uint32Array:
+        return speculationChecked(value.m_type, SpecUint32Array);
+
+    case Array::Float32Array:
+        return speculationChecked(value.m_type, SpecFloat32Array);
+
+    case Array::Float64Array:
+        return speculationChecked(value.m_type, SpecFloat64Array);
+        
+    case Array::SelectUsingPredictions:
+    case Array::Unprofiled:
+    case Array::Undecided:
+        break;
+    }
+    
+    CRASH();
+    return false;
+}
+
+const char* arrayTypeToString(Array::Type type)
+{
+    switch (type) {
+    case Array::SelectUsingPredictions:
+        return "SelectUsingPredictions";
+    case Array::Unprofiled:
+        return "Unprofiled";
+    case Array::Generic:
+        return "Generic";
+    case Array::ForceExit:
+        return "ForceExit";
+    case Array::String:
+        return "String";
+    case Array::Undecided:
+        return "Undecided";
+    case Array::Int32:
+        return "Int32";
+    case Array::Double:
+        return "Double";
+    case Array::Contiguous:
+        return "Contiguous";
+    case Array::ArrayStorage:
+        return "ArrayStorage";
+    case Array::SlowPutArrayStorage:
+        return "SlowPutArrayStorage";
+    case Array::Arguments:
+        return "Arguments";
+    case Array::Int8Array:
+        return "Int8Array";
+    case Array::Int16Array:
+        return "Int16Array";
+    case Array::Int32Array:
+        return "Int32Array";
+    case Array::Uint8Array:
+        return "Uint8Array";
+    case Array::Uint8ClampedArray:
+        return "Uint8ClampedArray";
+    case Array::Uint16Array:
+        return "Uint16Array";
+    case Array::Uint32Array:
+        return "Uint32Array";
+    case Array::Float32Array:
+        return "Float32Array";
+    case Array::Float64Array:
+        return "Float64Array";
+    default:
+        // Better to return something then it is to crash. Remember, this method
+        // is being called from our main diagnostic tool, the IR dumper. It's like
+        // a stack trace. So if we get here then probably something has already
+        // gone wrong.
+        return "Unknown!";
+    }
+}
+
+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:
+        return "PossiblyArray";
+    default:
+        return "Unknown!";
+    }
+}
+
+const char* arraySpeculationToString(Array::Speculation speculation)
+{
+    switch (speculation) {
+    case Array::SaneChain:
+        return "SaneChain";
+    case Array::InBounds:
+        return "InBounds";
+    case Array::ToHole:
+        return "ToHole";
+    case Array::OutOfBounds:
+        return "OutOfBounds";
+    default:
+        return "Unknown!";
+    }
+}
+
+const char* arrayConversionToString(Array::Conversion conversion)
+{
+    switch (conversion) {
+    case Array::AsIs:
+        return "AsIs";
+    case Array::Convert:
+        return "Convert";
+    case Array::RageConvert:
+        return "RageConvert";
+    default:
+        return "Unknown!";
+    }
+}
+
+void ArrayMode::dump(PrintStream& out) const
+{
+    out.print(type(), arrayClass(), speculation(), conversion());
+}
+
+} } // namespace JSC::DFG
+
+namespace WTF {
+
+void printInternal(PrintStream& out, JSC::DFG::Array::Type type)
+{
+    out.print(JSC::DFG::arrayTypeToString(type));
+}
+
+void printInternal(PrintStream& out, JSC::DFG::Array::Class arrayClass)
+{
+    out.print(JSC::DFG::arrayClassToString(arrayClass));
+}
+
+void printInternal(PrintStream& out, JSC::DFG::Array::Speculation speculation)
+{
+    out.print(JSC::DFG::arraySpeculationToString(speculation));
+}
+
+void printInternal(PrintStream& out, JSC::DFG::Array::Conversion conversion)
+{
+    out.print(JSC::DFG::arrayConversionToString(conversion));
+}
+
+} // namespace WTF
+
+#endif // ENABLE(DFG_JIT)
+