summaryrefslogtreecommitdiffstats
path: root/js/src/jit/TypedObjectPrediction.h
blob: 2e3caf2cf8641a1a5eff246a95bcf80a995f7efe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/* -*- 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>
    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