X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/a253471d7f8e4d91bf6ebabab00155c3b387d3d0..93a3786624b2768d89bfa27e46598dc64e2fb70a:/dfg/DFGArrayMode.cpp?ds=inline diff --git a/dfg/DFGArrayMode.cpp b/dfg/DFGArrayMode.cpp new file mode 100644 index 0000000..ede2ffc --- /dev/null +++ b/dfg/DFGArrayMode.cpp @@ -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) +