| /* |
| * 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" |
| |
| 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 (observed & (asArrayModes(ArrayWithUndecided) | asArrayModes(ArrayWithInt32) | asArrayModes(ArrayWithDouble) | asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage))) |
| arrayClass = Array::Array; |
| else if (observed & (asArrayModes(NonArray) | asArrayModes(NonArrayWithInt32) | asArrayModes(NonArrayWithDouble) | asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage))) |
| 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) || !isCellSpeculation(base)) |
| return ArrayMode(Array::Generic); |
| |
| 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 & NodeUsedAsIntLocally) |
| return withTypeAndConversion(Array::Contiguous, Array::RageConvert); |
| if (!value || isNumberSpeculation(value)) |
| return *this; |
| return withTypeAndConversion(Array::Contiguous, Array::Convert); |
| |
| case Array::Contiguous: |
| if (doesConversion() && (flags & NodeUsedAsIntLocally)) |
| return withConversion(Array::RageConvert); |
| return *this; |
| |
| case Array::SelectUsingPredictions: |
| 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) |
| |