Raw File
TypedObjectPrediction.h
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * 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>
  typename T::Type extractType() const;

  bool hasFieldNamedPrefix(const StructTypeDescr& descr, size_t fieldCount,
                           jsid id, size_t* fieldOffset,
                           TypedObjectPrediction* out, size_t* index,
                           bool* isMutable) const;

 public:
  ///////////////////////////////////////////////////////////////////////////
  // Constructing a prediction. Generally, you start with an empty
  // prediction and invoke addDescr() repeatedly.

  TypedObjectPrediction() : data_() { 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 or Reference.

  Scalar::Type scalarType() const;
  ReferenceType referenceType() 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,
                     bool* fieldMutable) const;
};

}  // namespace jit
}  // namespace js

#endif
back to top