/* -*- 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/. */ #ifndef jit_TypedObjectPrediction_h #define jit_TypedObjectPrediction_h #include "builtin/TypedObject.h" #include "jit/JitAllocPolicy.h" namespace js { namespace jit { // A TypedObjectPrediction summarizes what we know about the type of a // typed object at a given point (if anything). The prediction will // begin as precise as possible and degrade to less precise as more // typed object types are merged using |addDescr()|. // // To create a TypedObjectPrediction from TI, one initially creates an // empty prediction using the |TypedObjectPrediction()| constructor, // and then invokes |addDescr()| with the prototype of each typed // object. The prediction will automatically downgrade to less and // less specific settings as needed. Note that creating a prediction // in this way can never yield precise array dimensions, since TI only // tracks the prototype. // // TypedObjectPredictions can also result from other predictions using // the query methods (e.g., |arrayElementType()|). In those cases, the // precise array dimensions may be known. // // To query a prediction, you must first check whether it is "useless" // using |isUseless()|. If this is true, there is no usable // information to be extracted. Otherwise, you can inquire after the // |kind()| of the data (struct, array, etc) and from there make more // specific queries. class TypedObjectPrediction { public: enum PredictionKind { // No data. Empty, // Inconsistent data. Inconsistent, // Multiple different struct types flow into the same location, // but they share fields in common. Prefix indicates that the first // N fields of some struct type are known to be valid. This occurs // in a subtyping scenario. Prefix, // The TypeDescr of the value is known. This is the most specific // possible value and includes precise array bounds. Descr }; struct PrefixData { const StructTypeDescr* descr; size_t fields; }; union Data { const TypeDescr* descr; PrefixData prefix; }; private: PredictionKind kind_; Data data_; PredictionKind predictionKind() const { return kind_; } void markInconsistent() { kind_ = Inconsistent; } const TypeDescr& descr() const { MOZ_ASSERT(predictionKind() == Descr); return *data_.descr; } const PrefixData& prefix() const { MOZ_ASSERT(predictionKind() == Prefix); return data_.prefix; } void setDescr(const TypeDescr& descr) { kind_ = Descr; data_.descr = &descr; } void setPrefix(const StructTypeDescr& descr, size_t fields) { kind_ = Prefix; data_.prefix.descr = &descr; data_.prefix.fields = fields; } void markAsCommonPrefix(const StructTypeDescr& descrA, const StructTypeDescr& descrB, size_t max); template typename T::Type extractType() const; bool hasFieldNamedPrefix(const StructTypeDescr& descr, size_t fieldCount, jsid id, size_t* fieldOffset, TypedObjectPrediction* out, size_t* index) const; public: /////////////////////////////////////////////////////////////////////////// // Constructing a prediction. Generally, you start with an empty // prediction and invoke addDescr() repeatedly. TypedObjectPrediction() { kind_ = Empty; } explicit TypedObjectPrediction(const TypeDescr& descr) { setDescr(descr); } TypedObjectPrediction(const StructTypeDescr& descr, size_t fields) { setPrefix(descr, fields); } void addDescr(const TypeDescr& descr); /////////////////////////////////////////////////////////////////////////// // Queries that are always valid. bool isUseless() const { return predictionKind() == Empty || predictionKind() == Inconsistent; } // Determines whether we can predict the prototype for the typed // object instance. Returns null if we cannot or if the typed // object is of scalar/reference kind, in which case instances are // not objects and hence do not have a (publicly available) // prototype. const TypedProto* getKnownPrototype() const; /////////////////////////////////////////////////////////////////////////// // Queries that are valid if not useless. type::Kind kind() const; bool ofArrayKind() const; // Returns true if the size of this typed object is statically // known and sets |*out| to that size. Otherwise returns false. // // The size may not be statically known if (1) the object is // an array whose dimensions are unknown or (2) only a prefix // of its type is known. bool hasKnownSize(uint32_t* out) const; ////////////////////////////////////////////////////////////////////// // Simple operations // // Only valid when |kind()| is Scalar, Reference, or Simd (as appropriate). ScalarTypeDescr::Type scalarType() const; ReferenceTypeDescr::Type referenceType() const; SimdType simdType() const; /////////////////////////////////////////////////////////////////////////// // Queries valid only for arrays. // Returns true if the length of the array is statically known, // and sets |*length| appropriately. Otherwise returns false. bool hasKnownArrayLength(int32_t* length) const; // Returns a prediction for the array element type, if any. TypedObjectPrediction arrayElementType() const; ////////////////////////////////////////////////////////////////////// // Struct operations // // Only valid when |kind() == TypeDescr::Struct| // Returns true if the predicted type includes a field named |id| // and sets |*fieldOffset|, |*fieldType|, and |*fieldIndex| with // the offset (in bytes), type, and index of the field // respectively. Otherwise returns false. bool hasFieldNamed(jsid id, size_t* fieldOffset, TypedObjectPrediction* fieldType, size_t* fieldIndex) const; }; } // namespace jit } // namespace js #endif