ArrayMode ArrayMode::fromObserved(const ConcurrentJITLocker& locker, ArrayProfile* profile, Array::Action action, bool makeSafe) { Array::Class nonArray; if (profile->usesOriginalArrayStructures(locker)) nonArray = Array::OriginalNonArray; else nonArray = Array::NonArray; ArrayModes observed = profile->observedArrayModes(locker); switch (observed) { case 0: return ArrayMode(Array::Unprofiled); case asArrayModes(NonArray): if (action == Array::Write && !profile->mayInterceptIndexedAccesses(locker)) return ArrayMode(Array::Undecided, nonArray, Array::OutOfBounds, Array::Convert); return ArrayMode(Array::SelectUsingPredictions, nonArray).withSpeculationFromProfile(locker, profile, makeSafe); 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(locker)) return ArrayMode(Array::Undecided, Array::PossiblyArray, Array::OutOfBounds, Array::Convert); return ArrayMode(Array::SelectUsingPredictions).withSpeculationFromProfile(locker, profile, makeSafe); case asArrayModes(NonArrayWithInt32): return ArrayMode(Array::Int32, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(ArrayWithInt32): return ArrayMode(Array::Int32, Array::Array, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(NonArrayWithInt32) | asArrayModes(ArrayWithInt32): return ArrayMode(Array::Int32, Array::PossiblyArray, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(NonArrayWithDouble): return ArrayMode(Array::Double, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(ArrayWithDouble): return ArrayMode(Array::Double, Array::Array, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(NonArrayWithDouble) | asArrayModes(ArrayWithDouble): return ArrayMode(Array::Double, Array::PossiblyArray, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(NonArrayWithContiguous): return ArrayMode(Array::Contiguous, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(ArrayWithContiguous): return ArrayMode(Array::Contiguous, Array::Array, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous): return ArrayMode(Array::Contiguous, Array::PossiblyArray, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(NonArrayWithArrayStorage): return ArrayMode(Array::ArrayStorage, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(NonArrayWithSlowPutArrayStorage): case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage): return ArrayMode(Array::SlowPutArrayStorage, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(ArrayWithArrayStorage): return ArrayMode(Array::ArrayStorage, Array::Array, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(ArrayWithSlowPutArrayStorage): case asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage): return ArrayMode(Array::SlowPutArrayStorage, Array::Array, Array::AsIs).withProfile(locker, profile, makeSafe); case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage): return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::AsIs).withProfile(locker, 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(locker, profile, makeSafe); default: if ((observed & asArrayModes(NonArray)) && profile->mayInterceptIndexedAccesses(locker)) return ArrayMode(Array::SelectUsingPredictions).withSpeculationFromProfile(locker, profile, makeSafe); 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 = nonArray; else arrayClass = Array::PossiblyArray; return ArrayMode(type, arrayClass, Array::Convert).withProfile(locker, profile, makeSafe); } }
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( Graph& graph, CodeOrigin codeOrigin, 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 (isFullNumberSpeculation(value)) return withTypeAndConversion(Array::Double, Array::Convert); return withTypeAndConversion(Array::Contiguous, Array::Convert); case Array::Int32: if (!value || isInt32Speculation(value)) return *this; if (isFullNumberSpeculation(value)) return withTypeAndConversion(Array::Double, Array::Convert); return withTypeAndConversion(Array::Contiguous, Array::Convert); case Array::Double: if (flags & NodeBytecodeUsesAsInt) return withTypeAndConversion(Array::Contiguous, Array::RageConvert); if (!value || isFullNumberSpeculation(value)) return *this; return withTypeAndConversion(Array::Contiguous, Array::Convert); case Array::Contiguous: if (doesConversion() && (flags & NodeBytecodeUsesAsInt)) return withConversion(Array::RageConvert); return *this; case Array::SelectUsingPredictions: { base &= ~SpecOther; if (isStringSpeculation(base)) return withType(Array::String); if (isArgumentsSpeculation(base)) return withType(Array::Arguments); ArrayMode result; if (graph.hasExitSite(codeOrigin, OutOfBounds) || !isInBounds()) result = withSpeculation(Array::OutOfBounds); else result = withSpeculation(Array::InBounds); if (isInt8ArraySpeculation(base)) return result.withType(Array::Int8Array); if (isInt16ArraySpeculation(base)) return result.withType(Array::Int16Array); if (isInt32ArraySpeculation(base)) return result.withType(Array::Int32Array); if (isUint8ArraySpeculation(base)) return result.withType(Array::Uint8Array); if (isUint8ClampedArraySpeculation(base)) return result.withType(Array::Uint8ClampedArray); if (isUint16ArraySpeculation(base)) return result.withType(Array::Uint16Array); if (isUint32ArraySpeculation(base)) return result.withType(Array::Uint32Array); if (isFloat32ArraySpeculation(base)) return result.withType(Array::Float32Array); if (isFloat64ArraySpeculation(base)) return result.withType(Array::Float64Array); return ArrayMode(Array::Generic); } default: return *this; } }
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; } }