1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "jit/TypedObjectPrediction.h"
8 
9 using namespace js;
10 using namespace jit;
11 
12 static const size_t ALL_FIELDS = SIZE_MAX;
13 
14 // Sets the prediction to be the common prefix of descrA and descrB,
15 // considering at most the first max fields.
16 //
17 // In the case where the current prediction is a specific struct,
18 // and we are now seeing a second struct, then descrA and descrB will be
19 // the current and new struct and max will be ALL_FIELDS.
20 //
21 // In the case where the current prediction is already a prefix, and
22 // we are now seeing an additional struct, then descrA will be the
23 // current struct and max will be the current prefix length, and
24 // descrB will be the new struct.
25 //
26 // (Note that in general it is not important which struct is passed as
27 // descrA and which struct is passed as descrB, as the operation is
28 // symmetric.)
29 void
markAsCommonPrefix(const StructTypeDescr & descrA,const StructTypeDescr & descrB,size_t max)30 TypedObjectPrediction::markAsCommonPrefix(const StructTypeDescr& descrA,
31                                           const StructTypeDescr& descrB,
32                                           size_t max)
33 {
34     // count is the number of fields in common. It begins as the min
35     // of the number of fields from descrA, descrB, and max, and then
36     // is decremented as we find uncommon fields.
37     if (max > descrA.fieldCount())
38         max = descrA.fieldCount();
39     if (max > descrB.fieldCount())
40         max = descrB.fieldCount();
41 
42     size_t i = 0;
43     for (; i < max; i++) {
44         if (&descrA.fieldName(i) != &descrB.fieldName(i))
45             break;
46         if (&descrA.fieldDescr(i) != &descrB.fieldDescr(i))
47             break;
48         MOZ_ASSERT(descrA.fieldOffset(i) == descrB.fieldOffset(i));
49     }
50 
51     if (i == 0) {
52         // empty prefix is not particularly useful.
53         markInconsistent();
54     } else {
55         setPrefix(descrA, i);
56     }
57 }
58 
59 void
addDescr(const TypeDescr & descr)60 TypedObjectPrediction::addDescr(const TypeDescr& descr)
61 {
62     switch (predictionKind()) {
63       case Empty:
64         return setDescr(descr);
65 
66       case Inconsistent:
67         return; // keep same state
68 
69       case Descr: {
70         if (&descr == data_.descr)
71             return; // keep same state
72 
73         if (descr.kind() != data_.descr->kind())
74             return markInconsistent();
75 
76         if (descr.kind() != type::Struct)
77             return markInconsistent();
78 
79         const StructTypeDescr& structDescr = descr.as<StructTypeDescr>();
80         const StructTypeDescr& currentDescr = data_.descr->as<StructTypeDescr>();
81         markAsCommonPrefix(structDescr, currentDescr, ALL_FIELDS);
82         return;
83       }
84 
85       case Prefix:
86         if (descr.kind() != type::Struct)
87             return markInconsistent();
88 
89         markAsCommonPrefix(*data_.prefix.descr,
90                            descr.as<StructTypeDescr>(),
91                            data_.prefix.fields);
92         return;
93     }
94 
95     MOZ_CRASH("Bad predictionKind");
96 }
97 
98 type::Kind
kind() const99 TypedObjectPrediction::kind() const
100 {
101     switch (predictionKind()) {
102       case TypedObjectPrediction::Empty:
103       case TypedObjectPrediction::Inconsistent:
104         break;
105 
106       case TypedObjectPrediction::Descr:
107         return descr().kind();
108 
109       case TypedObjectPrediction::Prefix:
110         return prefix().descr->kind();
111     }
112 
113     MOZ_CRASH("Bad prediction kind");
114 }
115 
116 bool
ofArrayKind() const117 TypedObjectPrediction::ofArrayKind() const
118 {
119     switch (kind()) {
120       case type::Scalar:
121       case type::Reference:
122       case type::Simd:
123       case type::Struct:
124         return false;
125 
126       case type::Array:
127         return true;
128     }
129 
130     MOZ_CRASH("Bad kind");
131 }
132 
133 bool
hasKnownSize(uint32_t * out) const134 TypedObjectPrediction::hasKnownSize(uint32_t* out) const
135 {
136     switch (predictionKind()) {
137       case TypedObjectPrediction::Empty:
138       case TypedObjectPrediction::Inconsistent:
139         return false;
140 
141       case TypedObjectPrediction::Descr:
142         *out = descr().size();
143         return true;
144 
145       case TypedObjectPrediction::Prefix:
146         // We only know a prefix of the struct fields, hence we do not
147         // know its complete size.
148         return false;
149 
150       default:
151         MOZ_CRASH("Bad prediction kind");
152     }
153 }
154 
155 const TypedProto*
getKnownPrototype() const156 TypedObjectPrediction::getKnownPrototype() const
157 {
158     switch (predictionKind()) {
159       case TypedObjectPrediction::Empty:
160       case TypedObjectPrediction::Inconsistent:
161         return nullptr;
162 
163       case TypedObjectPrediction::Descr:
164         if (descr().is<ComplexTypeDescr>())
165             return &descr().as<ComplexTypeDescr>().instancePrototype();
166         return nullptr;
167 
168       case TypedObjectPrediction::Prefix:
169         // We only know a prefix of the struct fields, hence we cannot
170         // say for certain what its prototype will be.
171         return nullptr;
172 
173       default:
174         MOZ_CRASH("Bad prediction kind");
175     }
176 }
177 
178 template<typename T>
179 typename T::Type
extractType() const180 TypedObjectPrediction::extractType() const
181 {
182     MOZ_ASSERT(kind() == T::Kind);
183     switch (predictionKind()) {
184       case TypedObjectPrediction::Empty:
185       case TypedObjectPrediction::Inconsistent:
186         break;
187 
188       case TypedObjectPrediction::Descr:
189         return descr().as<T>().type();
190 
191       case TypedObjectPrediction::Prefix:
192         break; // Prefixes are always structs, never scalars etc
193     }
194 
195     MOZ_CRASH("Bad prediction kind");
196 }
197 
198 ScalarTypeDescr::Type
scalarType() const199 TypedObjectPrediction::scalarType() const
200 {
201     return extractType<ScalarTypeDescr>();
202 }
203 
204 ReferenceTypeDescr::Type
referenceType() const205 TypedObjectPrediction::referenceType() const
206 {
207     return extractType<ReferenceTypeDescr>();
208 }
209 
210 SimdType
simdType() const211 TypedObjectPrediction::simdType() const
212 {
213     return descr().as<SimdTypeDescr>().type();
214 }
215 
216 bool
hasKnownArrayLength(int32_t * length) const217 TypedObjectPrediction::hasKnownArrayLength(int32_t* length) const
218 {
219     switch (predictionKind()) {
220       case TypedObjectPrediction::Empty:
221       case TypedObjectPrediction::Inconsistent:
222         return false;
223 
224       case TypedObjectPrediction::Descr:
225         // In later patches, this condition will always be true
226         // so long as this represents an array
227         if (descr().is<ArrayTypeDescr>()) {
228             *length = descr().as<ArrayTypeDescr>().length();
229             return true;
230         }
231         return false;
232 
233       case TypedObjectPrediction::Prefix:
234         // Prefixes are always structs, never arrays
235         return false;
236 
237       default:
238         MOZ_CRASH("Bad prediction kind");
239     }
240 }
241 
242 TypedObjectPrediction
arrayElementType() const243 TypedObjectPrediction::arrayElementType() const
244 {
245     MOZ_ASSERT(ofArrayKind());
246     switch (predictionKind()) {
247       case TypedObjectPrediction::Empty:
248       case TypedObjectPrediction::Inconsistent:
249         break;
250 
251       case TypedObjectPrediction::Descr:
252         return TypedObjectPrediction(descr().as<ArrayTypeDescr>().elementType());
253 
254       case TypedObjectPrediction::Prefix:
255         break; // Prefixes are always structs, never arrays
256     }
257     MOZ_CRASH("Bad prediction kind");
258 }
259 
260 bool
hasFieldNamedPrefix(const StructTypeDescr & descr,size_t fieldCount,jsid id,size_t * fieldOffset,TypedObjectPrediction * out,size_t * index) const261 TypedObjectPrediction::hasFieldNamedPrefix(const StructTypeDescr& descr,
262                                            size_t fieldCount,
263                                            jsid id,
264                                            size_t* fieldOffset,
265                                            TypedObjectPrediction* out,
266                                            size_t* index) const
267 {
268     // Find the index of the field |id| if any.
269     if (!descr.fieldIndex(id, index))
270         return false;
271 
272     // Check whether the index falls within our known safe prefix.
273     if (*index >= fieldCount)
274         return false;
275 
276     // Load the offset and type.
277     *fieldOffset = descr.fieldOffset(*index);
278     *out = TypedObjectPrediction(descr.fieldDescr(*index));
279     return true;
280 }
281 
282 bool
hasFieldNamed(jsid id,size_t * fieldOffset,TypedObjectPrediction * fieldType,size_t * fieldIndex) const283 TypedObjectPrediction::hasFieldNamed(jsid id,
284                                      size_t* fieldOffset,
285                                      TypedObjectPrediction* fieldType,
286                                      size_t* fieldIndex) const
287 {
288     MOZ_ASSERT(kind() == type::Struct);
289 
290     switch (predictionKind()) {
291       case TypedObjectPrediction::Empty:
292       case TypedObjectPrediction::Inconsistent:
293         return false;
294 
295       case TypedObjectPrediction::Descr:
296         return hasFieldNamedPrefix(
297             descr().as<StructTypeDescr>(), ALL_FIELDS,
298             id, fieldOffset, fieldType, fieldIndex);
299 
300       case TypedObjectPrediction::Prefix:
301         return hasFieldNamedPrefix(
302             *prefix().descr, prefix().fields,
303             id, fieldOffset, fieldType, fieldIndex);
304 
305       default:
306         MOZ_CRASH("Bad prediction kind");
307     }
308 }
309