blob: e730482bf0899b0dd02c646cb46c5ccb06029d63 [file] [log] [blame]
/*
* 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)