blob: 4bc3fb7c85b6655607ead681c2ebeaa9da461786 [file] [log] [blame]
/* -*- 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<StructTypeDescr>();
const StructTypeDescr& currentDescr = data_.descr->as<StructTypeDescr>();
markAsCommonPrefix(structDescr, currentDescr, ALL_FIELDS);
return;
}
case Prefix:
if (descr.kind() != type::Struct)
return markInconsistent();
markAsCommonPrefix(*data_.prefix.descr,
descr.as<StructTypeDescr>(),
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(int32_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<ComplexTypeDescr>())
return &descr().as<ComplexTypeDescr>().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>
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<T>().type();
case TypedObjectPrediction::Prefix:
break; // Prefixes are always structs, never scalars etc
}
MOZ_CRASH("Bad prediction kind");
}
ScalarTypeDescr::Type
TypedObjectPrediction::scalarType() const
{
return extractType<ScalarTypeDescr>();
}
ReferenceTypeDescr::Type
TypedObjectPrediction::referenceType() const
{
return extractType<ReferenceTypeDescr>();
}
SimdTypeDescr::Type
TypedObjectPrediction::simdType() const
{
return extractType<SimdTypeDescr>();
}
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<ArrayTypeDescr>()) {
*length = descr().as<ArrayTypeDescr>().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<ArrayTypeDescr>().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<StructTypeDescr>(), 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");
}
}