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