js/src/jit/BaselineInspector.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:0931b7c51986
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/BaselineInspector.h"
8
9 #include "mozilla/DebugOnly.h"
10
11 #include "jit/BaselineIC.h"
12
13 using namespace js;
14 using namespace js::jit;
15
16 using mozilla::DebugOnly;
17
18 bool
19 SetElemICInspector::sawOOBDenseWrite() const
20 {
21 if (!icEntry_)
22 return false;
23
24 // Check for a SetElem_DenseAdd stub.
25 for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
26 if (stub->isSetElem_DenseAdd())
27 return true;
28 }
29
30 // Check for a write hole bit on the SetElem_Fallback stub.
31 ICStub *stub = icEntry_->fallbackStub();
32 if (stub->isSetElem_Fallback())
33 return stub->toSetElem_Fallback()->hasArrayWriteHole();
34
35 return false;
36 }
37
38 bool
39 SetElemICInspector::sawOOBTypedArrayWrite() const
40 {
41 if (!icEntry_)
42 return false;
43
44 // Check for SetElem_TypedArray stubs with expectOutOfBounds set.
45 for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
46 if (!stub->isSetElem_TypedArray())
47 continue;
48 if (stub->toSetElem_TypedArray()->expectOutOfBounds())
49 return true;
50 }
51 return false;
52 }
53
54 bool
55 SetElemICInspector::sawDenseWrite() const
56 {
57 if (!icEntry_)
58 return false;
59
60 // Check for a SetElem_DenseAdd or SetElem_Dense stub.
61 for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
62 if (stub->isSetElem_DenseAdd() || stub->isSetElem_Dense())
63 return true;
64 }
65 return false;
66 }
67
68 bool
69 SetElemICInspector::sawTypedArrayWrite() const
70 {
71 if (!icEntry_)
72 return false;
73
74 // Check for a SetElem_TypedArray stub.
75 for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
76 if (stub->isSetElem_TypedArray())
77 return true;
78 }
79 return false;
80 }
81
82 bool
83 BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes)
84 {
85 // Return a list of shapes seen by the baseline IC for the current op.
86 // An empty list indicates no shapes are known, or there was an uncacheable
87 // access.
88 JS_ASSERT(shapes.empty());
89
90 if (!hasBaselineScript())
91 return true;
92
93 JS_ASSERT(isValidPC(pc));
94 const ICEntry &entry = icEntryFromPC(pc);
95
96 ICStub *stub = entry.firstStub();
97 while (stub->next()) {
98 Shape *shape;
99 if (stub->isGetProp_Native()) {
100 shape = stub->toGetProp_Native()->shape();
101 } else if (stub->isSetProp_Native()) {
102 shape = stub->toSetProp_Native()->shape();
103 } else {
104 shapes.clear();
105 return true;
106 }
107
108 // Don't add the same shape twice (this can happen if there are multiple
109 // SetProp_Native stubs with different TypeObject's).
110 bool found = false;
111 for (size_t i = 0; i < shapes.length(); i++) {
112 if (shapes[i] == shape) {
113 found = true;
114 break;
115 }
116 }
117
118 if (!found && !shapes.append(shape))
119 return false;
120
121 stub = stub->next();
122 }
123
124 if (stub->isGetProp_Fallback()) {
125 if (stub->toGetProp_Fallback()->hadUnoptimizableAccess())
126 shapes.clear();
127 } else {
128 if (stub->toSetProp_Fallback()->hadUnoptimizableAccess())
129 shapes.clear();
130 }
131
132 // Don't inline if there are more than 5 shapes.
133 if (shapes.length() > 5)
134 shapes.clear();
135
136 return true;
137 }
138
139 ICStub *
140 BaselineInspector::monomorphicStub(jsbytecode *pc)
141 {
142 if (!hasBaselineScript())
143 return nullptr;
144
145 const ICEntry &entry = icEntryFromPC(pc);
146
147 ICStub *stub = entry.firstStub();
148 ICStub *next = stub->next();
149
150 if (!next || !next->isFallback())
151 return nullptr;
152
153 return stub;
154 }
155
156 bool
157 BaselineInspector::dimorphicStub(jsbytecode *pc, ICStub **pfirst, ICStub **psecond)
158 {
159 if (!hasBaselineScript())
160 return false;
161
162 const ICEntry &entry = icEntryFromPC(pc);
163
164 ICStub *stub = entry.firstStub();
165 ICStub *next = stub->next();
166 ICStub *after = next ? next->next() : nullptr;
167
168 if (!after || !after->isFallback())
169 return false;
170
171 *pfirst = stub;
172 *psecond = next;
173 return true;
174 }
175
176 MIRType
177 BaselineInspector::expectedResultType(jsbytecode *pc)
178 {
179 // Look at the IC entries for this op to guess what type it will produce,
180 // returning MIRType_None otherwise.
181
182 ICStub *stub = monomorphicStub(pc);
183 if (!stub)
184 return MIRType_None;
185
186 switch (stub->kind()) {
187 case ICStub::BinaryArith_Int32:
188 if (stub->toBinaryArith_Int32()->allowDouble())
189 return MIRType_Double;
190 return MIRType_Int32;
191 case ICStub::BinaryArith_BooleanWithInt32:
192 case ICStub::UnaryArith_Int32:
193 case ICStub::BinaryArith_DoubleWithInt32:
194 return MIRType_Int32;
195 case ICStub::BinaryArith_Double:
196 case ICStub::UnaryArith_Double:
197 return MIRType_Double;
198 case ICStub::BinaryArith_StringConcat:
199 case ICStub::BinaryArith_StringObjectConcat:
200 return MIRType_String;
201 default:
202 return MIRType_None;
203 }
204 }
205
206 // Whether a baseline stub kind is suitable for a double comparison that
207 // converts its operands to doubles.
208 static bool
209 CanUseDoubleCompare(ICStub::Kind kind)
210 {
211 return kind == ICStub::Compare_Double || kind == ICStub::Compare_NumberWithUndefined;
212 }
213
214 // Whether a baseline stub kind is suitable for an int32 comparison that
215 // converts its operands to int32.
216 static bool
217 CanUseInt32Compare(ICStub::Kind kind)
218 {
219 return kind == ICStub::Compare_Int32 || kind == ICStub::Compare_Int32WithBoolean;
220 }
221
222 MCompare::CompareType
223 BaselineInspector::expectedCompareType(jsbytecode *pc)
224 {
225 ICStub *first = monomorphicStub(pc), *second = nullptr;
226 if (!first && !dimorphicStub(pc, &first, &second))
227 return MCompare::Compare_Unknown;
228
229 if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind()))) {
230 ICCompare_Int32WithBoolean *coerce =
231 first->isCompare_Int32WithBoolean()
232 ? first->toCompare_Int32WithBoolean()
233 : ((second && second->isCompare_Int32WithBoolean())
234 ? second->toCompare_Int32WithBoolean()
235 : nullptr);
236 if (coerce) {
237 return coerce->lhsIsInt32()
238 ? MCompare::Compare_Int32MaybeCoerceRHS
239 : MCompare::Compare_Int32MaybeCoerceLHS;
240 }
241 return MCompare::Compare_Int32;
242 }
243
244 if (CanUseDoubleCompare(first->kind()) && (!second || CanUseDoubleCompare(second->kind()))) {
245 ICCompare_NumberWithUndefined *coerce =
246 first->isCompare_NumberWithUndefined()
247 ? first->toCompare_NumberWithUndefined()
248 : (second && second->isCompare_NumberWithUndefined())
249 ? second->toCompare_NumberWithUndefined()
250 : nullptr;
251 if (coerce) {
252 return coerce->lhsIsUndefined()
253 ? MCompare::Compare_DoubleMaybeCoerceLHS
254 : MCompare::Compare_DoubleMaybeCoerceRHS;
255 }
256 return MCompare::Compare_Double;
257 }
258
259 return MCompare::Compare_Unknown;
260 }
261
262 static bool
263 TryToSpecializeBinaryArithOp(ICStub **stubs,
264 uint32_t nstubs,
265 MIRType *result)
266 {
267 DebugOnly<bool> sawInt32 = false;
268 bool sawDouble = false;
269 bool sawOther = false;
270
271 for (uint32_t i = 0; i < nstubs; i++) {
272 switch (stubs[i]->kind()) {
273 case ICStub::BinaryArith_Int32:
274 sawInt32 = true;
275 break;
276 case ICStub::BinaryArith_BooleanWithInt32:
277 sawInt32 = true;
278 break;
279 case ICStub::BinaryArith_Double:
280 sawDouble = true;
281 break;
282 case ICStub::BinaryArith_DoubleWithInt32:
283 sawDouble = true;
284 break;
285 default:
286 sawOther = true;
287 break;
288 }
289 }
290
291 if (sawOther)
292 return false;
293
294 if (sawDouble) {
295 *result = MIRType_Double;
296 return true;
297 }
298
299 JS_ASSERT(sawInt32);
300 *result = MIRType_Int32;
301 return true;
302 }
303
304 MIRType
305 BaselineInspector::expectedBinaryArithSpecialization(jsbytecode *pc)
306 {
307 if (!hasBaselineScript())
308 return MIRType_None;
309
310 MIRType result;
311 ICStub *stubs[2];
312
313 const ICEntry &entry = icEntryFromPC(pc);
314 ICStub *stub = entry.fallbackStub();
315 if (stub->isBinaryArith_Fallback() &&
316 stub->toBinaryArith_Fallback()->hadUnoptimizableOperands())
317 {
318 return MIRType_None;
319 }
320
321 stubs[0] = monomorphicStub(pc);
322 if (stubs[0]) {
323 if (TryToSpecializeBinaryArithOp(stubs, 1, &result))
324 return result;
325 }
326
327 if (dimorphicStub(pc, &stubs[0], &stubs[1])) {
328 if (TryToSpecializeBinaryArithOp(stubs, 2, &result))
329 return result;
330 }
331
332 return MIRType_None;
333 }
334
335 bool
336 BaselineInspector::hasSeenNonNativeGetElement(jsbytecode *pc)
337 {
338 if (!hasBaselineScript())
339 return false;
340
341 const ICEntry &entry = icEntryFromPC(pc);
342 ICStub *stub = entry.fallbackStub();
343
344 if (stub->isGetElem_Fallback())
345 return stub->toGetElem_Fallback()->hasNonNativeAccess();
346 return false;
347 }
348
349 bool
350 BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode *pc)
351 {
352 if (!hasBaselineScript())
353 return false;
354
355 const ICEntry &entry = icEntryFromPC(pc);
356 ICStub *stub = entry.fallbackStub();
357
358 if (stub->isGetElem_Fallback())
359 return stub->toGetElem_Fallback()->hasNegativeIndex();
360 return false;
361 }
362
363 bool
364 BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc)
365 {
366 if (!hasBaselineScript())
367 return false;
368
369 const ICEntry &entry = icEntryFromPC(pc);
370 ICStub *stub = entry.fallbackStub();
371
372 if (stub->isGetProp_Fallback())
373 return stub->toGetProp_Fallback()->hasAccessedGetter();
374 return false;
375 }
376
377 bool
378 BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc)
379 {
380 JS_ASSERT(JSOp(*pc) == JSOP_ITERNEXT);
381
382 if (!hasBaselineScript())
383 return false;
384
385 const ICEntry &entry = icEntryFromPC(pc);
386 ICStub *stub = entry.fallbackStub();
387
388 return stub->toIteratorNext_Fallback()->hasNonStringResult();
389 }
390
391 bool
392 BaselineInspector::hasSeenDoubleResult(jsbytecode *pc)
393 {
394 if (!hasBaselineScript())
395 return false;
396
397 const ICEntry &entry = icEntryFromPC(pc);
398 ICStub *stub = entry.fallbackStub();
399
400 JS_ASSERT(stub->isUnaryArith_Fallback() || stub->isBinaryArith_Fallback());
401
402 if (stub->isUnaryArith_Fallback())
403 return stub->toUnaryArith_Fallback()->sawDoubleResult();
404 else
405 return stub->toBinaryArith_Fallback()->sawDoubleResult();
406
407 return false;
408 }
409
410 JSObject *
411 BaselineInspector::getTemplateObject(jsbytecode *pc)
412 {
413 if (!hasBaselineScript())
414 return nullptr;
415
416 const ICEntry &entry = icEntryFromPC(pc);
417 for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
418 switch (stub->kind()) {
419 case ICStub::NewArray_Fallback:
420 return stub->toNewArray_Fallback()->templateObject();
421 case ICStub::NewObject_Fallback:
422 return stub->toNewObject_Fallback()->templateObject();
423 case ICStub::Rest_Fallback:
424 return stub->toRest_Fallback()->templateObject();
425 case ICStub::Call_Scripted:
426 if (JSObject *obj = stub->toCall_Scripted()->templateObject())
427 return obj;
428 break;
429 default:
430 break;
431 }
432 }
433
434 return nullptr;
435 }
436
437 JSObject *
438 BaselineInspector::getTemplateObjectForNative(jsbytecode *pc, Native native)
439 {
440 if (!hasBaselineScript())
441 return nullptr;
442
443 const ICEntry &entry = icEntryFromPC(pc);
444 for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
445 if (stub->isCall_Native() && stub->toCall_Native()->callee()->native() == native)
446 return stub->toCall_Native()->templateObject();
447 }
448
449 return nullptr;
450 }
451
452 DeclEnvObject *
453 BaselineInspector::templateDeclEnvObject()
454 {
455 if (!hasBaselineScript())
456 return nullptr;
457
458 JSObject *res = &templateCallObject()->as<ScopeObject>().enclosingScope();
459 JS_ASSERT(res);
460
461 return &res->as<DeclEnvObject>();
462 }
463
464 CallObject *
465 BaselineInspector::templateCallObject()
466 {
467 if (!hasBaselineScript())
468 return nullptr;
469
470 JSObject *res = baselineScript()->templateScope();
471 JS_ASSERT(res);
472
473 return &res->as<CallObject>();
474 }
475
476 JSObject *
477 BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter)
478 {
479 if (!hasBaselineScript())
480 return nullptr;
481
482 const ICEntry &entry = icEntryFromPC(pc);
483 for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
484 if (stub->isGetProp_CallScripted() ||
485 stub->isGetProp_CallNative() ||
486 stub->isGetProp_CallNativePrototype())
487 {
488 ICGetPropCallGetter *nstub = static_cast<ICGetPropCallGetter *>(stub);
489 *lastProperty = nstub->holderShape();
490 *commonGetter = nstub->getter();
491 return nstub->holder();
492 }
493 }
494 return nullptr;
495 }
496
497 JSObject *
498 BaselineInspector::commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter)
499 {
500 if (!hasBaselineScript())
501 return nullptr;
502
503 const ICEntry &entry = icEntryFromPC(pc);
504 for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
505 if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
506 ICSetPropCallSetter *nstub = static_cast<ICSetPropCallSetter *>(stub);
507 *lastProperty = nstub->holderShape();
508 *commonSetter = nstub->setter();
509 return nstub->holder();
510 }
511 }
512 return nullptr;
513 }

mercurial