/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "jit/TypedObjectPrediction.h" using namespace js; using namespace jit; static const size_t ALL_FIELDS = SIZE_MAX; // Sets the prediction to be the common prefix of descrA and descrB, // considering at most the first max fields. // // In the case where the current prediction is a specific struct, // and we are now seeing a second struct, then descrA and descrB will be // the current and new struct and max will be ALL_FIELDS. // // In the case where the current prediction is already a prefix, and // we are now seeing an additional struct, then descrA will be the // current struct and max will be the current prefix length, and // descrB will be the new struct. // // (Note that in general it is not important which struct is passed as // descrA and which struct is passed as descrB, as the operation is // symmetric.) void TypedObjectPrediction::markAsCommonPrefix(const StructTypeDescr& descrA, const StructTypeDescr& descrB, size_t max) { // count is the number of fields in common. It begins as the min // of the number of fields from descrA, descrB, and max, and then // is decremented as we find uncommon fields. if (max > descrA.fieldCount()) max = descrA.fieldCount(); if (max > descrB.fieldCount()) max = descrB.fieldCount(); size_t i = 0; for (; i < max; i++) { if (&descrA.fieldName(i) != &descrB.fieldName(i)) break; if (&descrA.fieldDescr(i) != &descrB.fieldDescr(i)) break; MOZ_ASSERT(descrA.fieldOffset(i) == descrB.fieldOffset(i)); } if (i == 0) { // empty prefix is not particularly useful. markInconsistent(); } else { setPrefix(descrA, i); } } void TypedObjectPrediction::addDescr(const TypeDescr& descr) { switch (predictionKind()) { case Empty: return setDescr(descr); case Inconsistent: return; // keep same state case Descr: { if (&descr == data_.descr) return; // keep same state if (descr.kind() != data_.descr->kind()) return markInconsistent(); if (descr.kind() != type::Struct) return markInconsistent(); const StructTypeDescr& structDescr = descr.as(); const StructTypeDescr& currentDescr = data_.descr->as(); markAsCommonPrefix(structDescr, currentDescr, ALL_FIELDS); return; } case Prefix: if (descr.kind() != type::Struct) return markInconsistent(); markAsCommonPrefix(*data_.prefix.descr, descr.as(), data_.prefix.fields); return; } MOZ_CRASH("Bad predictionKind"); } type::Kind TypedObjectPrediction::kind() const { switch (predictionKind()) { case TypedObjectPrediction::Empty: case TypedObjectPrediction::Inconsistent: break; case TypedObjectPrediction::Descr: return descr().kind(); case TypedObjectPrediction::Prefix: return prefix().descr->kind(); } MOZ_CRASH("Bad prediction kind"); } bool TypedObjectPrediction::ofArrayKind() const { switch (kind()) { case type::Scalar: case type::Reference: case type::Simd: case type::Struct: return false; case type::Array: return true; } MOZ_CRASH("Bad kind"); } bool TypedObjectPrediction::hasKnownSize(uint32_t* out) const { switch (predictionKind()) { case TypedObjectPrediction::Empty: case TypedObjectPrediction::Inconsistent: return false; case TypedObjectPrediction::Descr: *out = descr().size(); return true; case TypedObjectPrediction::Prefix: // We only know a prefix of the struct fields, hence we do not // know its complete size. return false; default: MOZ_CRASH("Bad prediction kind"); } } const TypedProto* TypedObjectPrediction::getKnownPrototype() const { switch (predictionKind()) { case TypedObjectPrediction::Empty: case TypedObjectPrediction::Inconsistent: return nullptr; case TypedObjectPrediction::Descr: if (descr().is()) return &descr().as().instancePrototype(); return nullptr; case TypedObjectPrediction::Prefix: // We only know a prefix of the struct fields, hence we cannot // say for certain what its prototype will be. return nullptr; default: MOZ_CRASH("Bad prediction kind"); } } template typename T::Type TypedObjectPrediction::extractType() const { MOZ_ASSERT(kind() == T::Kind); switch (predictionKind()) { case TypedObjectPrediction::Empty: case TypedObjectPrediction::Inconsistent: break; case TypedObjectPrediction::Descr: return descr().as().type(); case TypedObjectPrediction::Prefix: break; // Prefixes are always structs, never scalars etc } MOZ_CRASH("Bad prediction kind"); } ScalarTypeDescr::Type TypedObjectPrediction::scalarType() const { return extractType(); } ReferenceTypeDescr::Type TypedObjectPrediction::referenceType() const { return extractType(); } SimdType TypedObjectPrediction::simdType() const { return descr().as().type(); } bool TypedObjectPrediction::hasKnownArrayLength(int32_t* length) const { switch (predictionKind()) { case TypedObjectPrediction::Empty: case TypedObjectPrediction::Inconsistent: return false; case TypedObjectPrediction::Descr: // In later patches, this condition will always be true // so long as this represents an array if (descr().is()) { *length = descr().as().length(); return true; } return false; case TypedObjectPrediction::Prefix: // Prefixes are always structs, never arrays return false; default: MOZ_CRASH("Bad prediction kind"); } } TypedObjectPrediction TypedObjectPrediction::arrayElementType() const { MOZ_ASSERT(ofArrayKind()); switch (predictionKind()) { case TypedObjectPrediction::Empty: case TypedObjectPrediction::Inconsistent: break; case TypedObjectPrediction::Descr: return TypedObjectPrediction(descr().as().elementType()); case TypedObjectPrediction::Prefix: break; // Prefixes are always structs, never arrays } MOZ_CRASH("Bad prediction kind"); } bool TypedObjectPrediction::hasFieldNamedPrefix(const StructTypeDescr& descr, size_t fieldCount, jsid id, size_t* fieldOffset, TypedObjectPrediction* out, size_t* index) const { // Find the index of the field |id| if any. if (!descr.fieldIndex(id, index)) return false; // Check whether the index falls within our known safe prefix. if (*index >= fieldCount) return false; // Load the offset and type. *fieldOffset = descr.fieldOffset(*index); *out = TypedObjectPrediction(descr.fieldDescr(*index)); return true; } bool TypedObjectPrediction::hasFieldNamed(jsid id, size_t* fieldOffset, TypedObjectPrediction* fieldType, size_t* fieldIndex) const { MOZ_ASSERT(kind() == type::Struct); switch (predictionKind()) { case TypedObjectPrediction::Empty: case TypedObjectPrediction::Inconsistent: return false; case TypedObjectPrediction::Descr: return hasFieldNamedPrefix( descr().as(), ALL_FIELDS, id, fieldOffset, fieldType, fieldIndex); case TypedObjectPrediction::Prefix: return hasFieldNamedPrefix( *prefix().descr, prefix().fields, id, fieldOffset, fieldType, fieldIndex); default: MOZ_CRASH("Bad prediction kind"); } }