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 #include "vm/ObjectGroup-inl.h"
14
15 using namespace js;
16 using namespace js::jit;
17
18 using mozilla::DebugOnly;
19
20 bool
sawOOBDenseWrite() const21 SetElemICInspector::sawOOBDenseWrite() const
22 {
23 if (!icEntry_)
24 return false;
25
26 // Check for an element adding stub.
27 for (ICStub* stub = icEntry_->firstStub(); stub; stub = stub->next()) {
28 if (stub->isSetElem_DenseOrUnboxedArrayAdd())
29 return true;
30 }
31
32 // Check for a write hole bit on the SetElem_Fallback stub.
33 ICStub* stub = icEntry_->fallbackStub();
34 if (stub->isSetElem_Fallback())
35 return stub->toSetElem_Fallback()->hasArrayWriteHole();
36
37 return false;
38 }
39
40 bool
sawOOBTypedArrayWrite() const41 SetElemICInspector::sawOOBTypedArrayWrite() const
42 {
43 if (!icEntry_)
44 return false;
45
46 // Check for SetElem_TypedArray stubs with expectOutOfBounds set.
47 for (ICStub* stub = icEntry_->firstStub(); stub; stub = stub->next()) {
48 if (!stub->isSetElem_TypedArray())
49 continue;
50 if (stub->toSetElem_TypedArray()->expectOutOfBounds())
51 return true;
52 }
53 return false;
54 }
55
56 bool
sawDenseWrite() const57 SetElemICInspector::sawDenseWrite() const
58 {
59 if (!icEntry_)
60 return false;
61
62 // Check for a SetElem_DenseAdd or SetElem_Dense stub.
63 for (ICStub* stub = icEntry_->firstStub(); stub; stub = stub->next()) {
64 if (stub->isSetElem_DenseOrUnboxedArrayAdd() || stub->isSetElem_DenseOrUnboxedArray())
65 return true;
66 }
67 return false;
68 }
69
70 bool
sawTypedArrayWrite() const71 SetElemICInspector::sawTypedArrayWrite() const
72 {
73 if (!icEntry_)
74 return false;
75
76 // Check for a SetElem_TypedArray stub.
77 for (ICStub* stub = icEntry_->firstStub(); stub; stub = stub->next()) {
78 if (stub->isSetElem_TypedArray())
79 return true;
80 }
81 return false;
82 }
83
84 template <typename S, typename T>
85 static bool
VectorAppendNoDuplicate(S & list,T value)86 VectorAppendNoDuplicate(S& list, T value)
87 {
88 for (size_t i = 0; i < list.length(); i++) {
89 if (list[i] == value)
90 return true;
91 }
92 return list.append(value);
93 }
94
95 static bool
AddReceiver(const ReceiverGuard & receiver,BaselineInspector::ReceiverVector & receivers,BaselineInspector::ObjectGroupVector & convertUnboxedGroups)96 AddReceiver(const ReceiverGuard& receiver,
97 BaselineInspector::ReceiverVector& receivers,
98 BaselineInspector::ObjectGroupVector& convertUnboxedGroups)
99 {
100 if (receiver.group && receiver.group->maybeUnboxedLayout()) {
101 if (receiver.group->unboxedLayout().nativeGroup())
102 return VectorAppendNoDuplicate(convertUnboxedGroups, receiver.group);
103 }
104 return VectorAppendNoDuplicate(receivers, receiver);
105 }
106
107 bool
maybeInfoForPropertyOp(jsbytecode * pc,ReceiverVector & receivers,ObjectGroupVector & convertUnboxedGroups)108 BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers,
109 ObjectGroupVector& convertUnboxedGroups)
110 {
111 // Return a list of the receivers seen by the baseline IC for the current
112 // op. Empty lists indicate no receivers are known, or there was an
113 // uncacheable access. convertUnboxedGroups is used for unboxed object
114 // groups which have been seen, but have had instances converted to native
115 // objects and should be eagerly converted by Ion.
116 MOZ_ASSERT(receivers.empty());
117 MOZ_ASSERT(convertUnboxedGroups.empty());
118
119 if (!hasBaselineScript())
120 return true;
121
122 MOZ_ASSERT(isValidPC(pc));
123 const ICEntry& entry = icEntryFromPC(pc);
124
125 ICStub* stub = entry.firstStub();
126 while (stub->next()) {
127 ReceiverGuard receiver;
128 if (stub->isGetProp_Native()) {
129 receiver = stub->toGetProp_Native()->receiverGuard();
130 } else if (stub->isSetProp_Native()) {
131 receiver = ReceiverGuard(stub->toSetProp_Native()->group(),
132 stub->toSetProp_Native()->shape());
133 } else if (stub->isGetProp_Unboxed()) {
134 receiver = ReceiverGuard(stub->toGetProp_Unboxed()->group(), nullptr);
135 } else if (stub->isSetProp_Unboxed()) {
136 receiver = ReceiverGuard(stub->toSetProp_Unboxed()->group(), nullptr);
137 } else {
138 receivers.clear();
139 return true;
140 }
141
142 if (!AddReceiver(receiver, receivers, convertUnboxedGroups))
143 return false;
144
145 stub = stub->next();
146 }
147
148 if (stub->isGetProp_Fallback()) {
149 if (stub->toGetProp_Fallback()->hadUnoptimizableAccess())
150 receivers.clear();
151 } else {
152 if (stub->toSetProp_Fallback()->hadUnoptimizableAccess())
153 receivers.clear();
154 }
155
156 // Don't inline if there are more than 5 receivers.
157 if (receivers.length() > 5)
158 receivers.clear();
159
160 return true;
161 }
162
163 ICStub*
monomorphicStub(jsbytecode * pc)164 BaselineInspector::monomorphicStub(jsbytecode* pc)
165 {
166 if (!hasBaselineScript())
167 return nullptr;
168
169 const ICEntry& entry = icEntryFromPC(pc);
170
171 ICStub* stub = entry.firstStub();
172 ICStub* next = stub->next();
173
174 if (!next || !next->isFallback())
175 return nullptr;
176
177 return stub;
178 }
179
180 bool
dimorphicStub(jsbytecode * pc,ICStub ** pfirst,ICStub ** psecond)181 BaselineInspector::dimorphicStub(jsbytecode* pc, ICStub** pfirst, ICStub** psecond)
182 {
183 if (!hasBaselineScript())
184 return false;
185
186 const ICEntry& entry = icEntryFromPC(pc);
187
188 ICStub* stub = entry.firstStub();
189 ICStub* next = stub->next();
190 ICStub* after = next ? next->next() : nullptr;
191
192 if (!after || !after->isFallback())
193 return false;
194
195 *pfirst = stub;
196 *psecond = next;
197 return true;
198 }
199
200 MIRType
expectedResultType(jsbytecode * pc)201 BaselineInspector::expectedResultType(jsbytecode* pc)
202 {
203 // Look at the IC entries for this op to guess what type it will produce,
204 // returning MIRType_None otherwise.
205
206 ICStub* stub = monomorphicStub(pc);
207 if (!stub)
208 return MIRType_None;
209
210 switch (stub->kind()) {
211 case ICStub::BinaryArith_Int32:
212 if (stub->toBinaryArith_Int32()->allowDouble())
213 return MIRType_Double;
214 return MIRType_Int32;
215 case ICStub::BinaryArith_BooleanWithInt32:
216 case ICStub::UnaryArith_Int32:
217 case ICStub::BinaryArith_DoubleWithInt32:
218 return MIRType_Int32;
219 case ICStub::BinaryArith_Double:
220 case ICStub::UnaryArith_Double:
221 return MIRType_Double;
222 case ICStub::BinaryArith_StringConcat:
223 case ICStub::BinaryArith_StringObjectConcat:
224 return MIRType_String;
225 default:
226 return MIRType_None;
227 }
228 }
229
230 // Whether a baseline stub kind is suitable for a double comparison that
231 // converts its operands to doubles.
232 static bool
CanUseDoubleCompare(ICStub::Kind kind)233 CanUseDoubleCompare(ICStub::Kind kind)
234 {
235 return kind == ICStub::Compare_Double || kind == ICStub::Compare_NumberWithUndefined;
236 }
237
238 // Whether a baseline stub kind is suitable for an int32 comparison that
239 // converts its operands to int32.
240 static bool
CanUseInt32Compare(ICStub::Kind kind)241 CanUseInt32Compare(ICStub::Kind kind)
242 {
243 return kind == ICStub::Compare_Int32 || kind == ICStub::Compare_Int32WithBoolean;
244 }
245
246 MCompare::CompareType
expectedCompareType(jsbytecode * pc)247 BaselineInspector::expectedCompareType(jsbytecode* pc)
248 {
249 ICStub* first = monomorphicStub(pc);
250 ICStub* second = nullptr;
251 if (!first && !dimorphicStub(pc, &first, &second))
252 return MCompare::Compare_Unknown;
253
254 if (ICStub* fallback = second ? second->next() : first->next()) {
255 MOZ_ASSERT(fallback->isFallback());
256 if (fallback->toCompare_Fallback()->hadUnoptimizableAccess())
257 return MCompare::Compare_Unknown;
258 }
259
260 if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind()))) {
261 ICCompare_Int32WithBoolean* coerce =
262 first->isCompare_Int32WithBoolean()
263 ? first->toCompare_Int32WithBoolean()
264 : ((second && second->isCompare_Int32WithBoolean())
265 ? second->toCompare_Int32WithBoolean()
266 : nullptr);
267 if (coerce) {
268 return coerce->lhsIsInt32()
269 ? MCompare::Compare_Int32MaybeCoerceRHS
270 : MCompare::Compare_Int32MaybeCoerceLHS;
271 }
272 return MCompare::Compare_Int32;
273 }
274
275 if (CanUseDoubleCompare(first->kind()) && (!second || CanUseDoubleCompare(second->kind()))) {
276 ICCompare_NumberWithUndefined* coerce =
277 first->isCompare_NumberWithUndefined()
278 ? first->toCompare_NumberWithUndefined()
279 : (second && second->isCompare_NumberWithUndefined())
280 ? second->toCompare_NumberWithUndefined()
281 : nullptr;
282 if (coerce) {
283 return coerce->lhsIsUndefined()
284 ? MCompare::Compare_DoubleMaybeCoerceLHS
285 : MCompare::Compare_DoubleMaybeCoerceRHS;
286 }
287 return MCompare::Compare_Double;
288 }
289
290 return MCompare::Compare_Unknown;
291 }
292
293 static bool
TryToSpecializeBinaryArithOp(ICStub ** stubs,uint32_t nstubs,MIRType * result)294 TryToSpecializeBinaryArithOp(ICStub** stubs,
295 uint32_t nstubs,
296 MIRType* result)
297 {
298 DebugOnly<bool> sawInt32 = false;
299 bool sawDouble = false;
300 bool sawOther = false;
301
302 for (uint32_t i = 0; i < nstubs; i++) {
303 switch (stubs[i]->kind()) {
304 case ICStub::BinaryArith_Int32:
305 sawInt32 = true;
306 break;
307 case ICStub::BinaryArith_BooleanWithInt32:
308 sawInt32 = true;
309 break;
310 case ICStub::BinaryArith_Double:
311 sawDouble = true;
312 break;
313 case ICStub::BinaryArith_DoubleWithInt32:
314 sawDouble = true;
315 break;
316 default:
317 sawOther = true;
318 break;
319 }
320 }
321
322 if (sawOther)
323 return false;
324
325 if (sawDouble) {
326 *result = MIRType_Double;
327 return true;
328 }
329
330 MOZ_ASSERT(sawInt32);
331 *result = MIRType_Int32;
332 return true;
333 }
334
335 MIRType
expectedBinaryArithSpecialization(jsbytecode * pc)336 BaselineInspector::expectedBinaryArithSpecialization(jsbytecode* pc)
337 {
338 if (!hasBaselineScript())
339 return MIRType_None;
340
341 MIRType result;
342 ICStub* stubs[2];
343
344 const ICEntry& entry = icEntryFromPC(pc);
345 ICStub* stub = entry.fallbackStub();
346 if (stub->isBinaryArith_Fallback() &&
347 stub->toBinaryArith_Fallback()->hadUnoptimizableOperands())
348 {
349 return MIRType_None;
350 }
351
352 stubs[0] = monomorphicStub(pc);
353 if (stubs[0]) {
354 if (TryToSpecializeBinaryArithOp(stubs, 1, &result))
355 return result;
356 }
357
358 if (dimorphicStub(pc, &stubs[0], &stubs[1])) {
359 if (TryToSpecializeBinaryArithOp(stubs, 2, &result))
360 return result;
361 }
362
363 return MIRType_None;
364 }
365
366 bool
hasSeenNonNativeGetElement(jsbytecode * pc)367 BaselineInspector::hasSeenNonNativeGetElement(jsbytecode* pc)
368 {
369 if (!hasBaselineScript())
370 return false;
371
372 const ICEntry& entry = icEntryFromPC(pc);
373 ICStub* stub = entry.fallbackStub();
374
375 if (stub->isGetElem_Fallback())
376 return stub->toGetElem_Fallback()->hasNonNativeAccess();
377 return false;
378 }
379
380 bool
hasSeenNegativeIndexGetElement(jsbytecode * pc)381 BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode* pc)
382 {
383 if (!hasBaselineScript())
384 return false;
385
386 const ICEntry& entry = icEntryFromPC(pc);
387 ICStub* stub = entry.fallbackStub();
388
389 if (stub->isGetElem_Fallback())
390 return stub->toGetElem_Fallback()->hasNegativeIndex();
391 return false;
392 }
393
394 bool
hasSeenAccessedGetter(jsbytecode * pc)395 BaselineInspector::hasSeenAccessedGetter(jsbytecode* pc)
396 {
397 if (!hasBaselineScript())
398 return false;
399
400 const ICEntry& entry = icEntryFromPC(pc);
401 ICStub* stub = entry.fallbackStub();
402
403 if (stub->isGetProp_Fallback())
404 return stub->toGetProp_Fallback()->hasAccessedGetter();
405 return false;
406 }
407
408 bool
hasSeenNonStringIterMore(jsbytecode * pc)409 BaselineInspector::hasSeenNonStringIterMore(jsbytecode* pc)
410 {
411 MOZ_ASSERT(JSOp(*pc) == JSOP_MOREITER);
412
413 if (!hasBaselineScript())
414 return false;
415
416 const ICEntry& entry = icEntryFromPC(pc);
417 ICStub* stub = entry.fallbackStub();
418
419 return stub->toIteratorMore_Fallback()->hasNonStringResult();
420 }
421
422 bool
hasSeenDoubleResult(jsbytecode * pc)423 BaselineInspector::hasSeenDoubleResult(jsbytecode* pc)
424 {
425 if (!hasBaselineScript())
426 return false;
427
428 const ICEntry& entry = icEntryFromPC(pc);
429 ICStub* stub = entry.fallbackStub();
430
431 MOZ_ASSERT(stub->isUnaryArith_Fallback() || stub->isBinaryArith_Fallback());
432
433 if (stub->isUnaryArith_Fallback())
434 return stub->toUnaryArith_Fallback()->sawDoubleResult();
435 else
436 return stub->toBinaryArith_Fallback()->sawDoubleResult();
437
438 return false;
439 }
440
441 JSObject*
getTemplateObject(jsbytecode * pc)442 BaselineInspector::getTemplateObject(jsbytecode* pc)
443 {
444 if (!hasBaselineScript())
445 return nullptr;
446
447 const ICEntry& entry = icEntryFromPC(pc);
448 for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
449 switch (stub->kind()) {
450 case ICStub::NewArray_Fallback:
451 return stub->toNewArray_Fallback()->templateObject();
452 case ICStub::NewObject_Fallback:
453 return stub->toNewObject_Fallback()->templateObject();
454 case ICStub::Rest_Fallback:
455 return stub->toRest_Fallback()->templateObject();
456 case ICStub::Call_Scripted:
457 if (JSObject* obj = stub->toCall_Scripted()->templateObject())
458 return obj;
459 break;
460 default:
461 break;
462 }
463 }
464
465 return nullptr;
466 }
467
468 ObjectGroup*
getTemplateObjectGroup(jsbytecode * pc)469 BaselineInspector::getTemplateObjectGroup(jsbytecode* pc)
470 {
471 if (!hasBaselineScript())
472 return nullptr;
473
474 const ICEntry& entry = icEntryFromPC(pc);
475 for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
476 switch (stub->kind()) {
477 case ICStub::NewArray_Fallback:
478 return stub->toNewArray_Fallback()->templateGroup();
479 default:
480 break;
481 }
482 }
483
484 return nullptr;
485 }
486
487 JSFunction*
getSingleCallee(jsbytecode * pc)488 BaselineInspector::getSingleCallee(jsbytecode* pc)
489 {
490 MOZ_ASSERT(*pc == JSOP_NEW);
491
492 if (!hasBaselineScript())
493 return nullptr;
494
495 const ICEntry& entry = icEntryFromPC(pc);
496 ICStub* stub = entry.firstStub();
497
498 if (entry.fallbackStub()->toCall_Fallback()->hadUnoptimizableCall())
499 return nullptr;
500
501 if (!stub->isCall_Scripted() || stub->next() != entry.fallbackStub())
502 return nullptr;
503
504 return stub->toCall_Scripted()->callee();
505 }
506
507 JSObject*
getTemplateObjectForNative(jsbytecode * pc,Native native)508 BaselineInspector::getTemplateObjectForNative(jsbytecode* pc, Native native)
509 {
510 if (!hasBaselineScript())
511 return nullptr;
512
513 const ICEntry& entry = icEntryFromPC(pc);
514 for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
515 if (stub->isCall_Native() && stub->toCall_Native()->callee()->native() == native)
516 return stub->toCall_Native()->templateObject();
517 }
518
519 return nullptr;
520 }
521
522 bool
isOptimizableCallStringSplit(jsbytecode * pc,JSString ** stringOut,JSString ** stringArg,JSObject ** objOut)523 BaselineInspector::isOptimizableCallStringSplit(jsbytecode* pc, JSString** stringOut, JSString** stringArg,
524 JSObject** objOut)
525 {
526 if (!hasBaselineScript())
527 return false;
528
529 const ICEntry& entry = icEntryFromPC(pc);
530
531 // If StringSplit stub is attached, must have only one stub attached.
532 if (entry.fallbackStub()->numOptimizedStubs() != 1)
533 return false;
534
535 ICStub* stub = entry.firstStub();
536 if (stub->kind() != ICStub::Call_StringSplit)
537 return false;
538
539 *stringOut = stub->toCall_StringSplit()->expectedThis();
540 *stringArg = stub->toCall_StringSplit()->expectedArg();
541 *objOut = stub->toCall_StringSplit()->templateObject();
542 return true;
543 }
544
545 JSObject*
getTemplateObjectForClassHook(jsbytecode * pc,const Class * clasp)546 BaselineInspector::getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp)
547 {
548 if (!hasBaselineScript())
549 return nullptr;
550
551 const ICEntry& entry = icEntryFromPC(pc);
552 for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
553 if (stub->isCall_ClassHook() && stub->toCall_ClassHook()->clasp() == clasp)
554 return stub->toCall_ClassHook()->templateObject();
555 }
556
557 return nullptr;
558 }
559
560 DeclEnvObject*
templateDeclEnvObject()561 BaselineInspector::templateDeclEnvObject()
562 {
563 if (!hasBaselineScript())
564 return nullptr;
565
566 JSObject* res = &templateCallObject()->as<ScopeObject>().enclosingScope();
567 MOZ_ASSERT(res);
568
569 return &res->as<DeclEnvObject>();
570 }
571
572 CallObject*
templateCallObject()573 BaselineInspector::templateCallObject()
574 {
575 if (!hasBaselineScript())
576 return nullptr;
577
578 JSObject* res = baselineScript()->templateScope();
579 MOZ_ASSERT(res);
580
581 return &res->as<CallObject>();
582 }
583
584 static Shape*
GlobalShapeForGetPropFunction(ICStub * stub)585 GlobalShapeForGetPropFunction(ICStub* stub)
586 {
587 if (stub->isGetProp_CallNative()) {
588 ICGetProp_CallNative* nstub = stub->toGetProp_CallNative();
589 if (nstub->isOwnGetter())
590 return nullptr;
591
592 const HeapReceiverGuard& guard = nstub->receiverGuard();
593 if (Shape* shape = guard.shape()) {
594 if (shape->getObjectClass()->flags & JSCLASS_IS_GLOBAL)
595 return shape;
596 }
597 } else if (stub->isGetProp_CallNativeGlobal()) {
598 ICGetProp_CallNativeGlobal* nstub = stub->toGetProp_CallNativeGlobal();
599 if (nstub->isOwnGetter())
600 return nullptr;
601
602 Shape* shape = nstub->globalShape();
603 MOZ_ASSERT(shape->getObjectClass()->flags & JSCLASS_IS_GLOBAL);
604 return shape;
605 }
606
607 return nullptr;
608 }
609
610 bool
commonGetPropFunction(jsbytecode * pc,JSObject ** holder,Shape ** holderShape,JSFunction ** commonGetter,Shape ** globalShape,bool * isOwnProperty,ReceiverVector & receivers,ObjectGroupVector & convertUnboxedGroups)611 BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
612 JSFunction** commonGetter, Shape** globalShape,
613 bool* isOwnProperty,
614 ReceiverVector& receivers,
615 ObjectGroupVector& convertUnboxedGroups)
616 {
617 if (!hasBaselineScript())
618 return false;
619
620 MOZ_ASSERT(receivers.empty());
621 MOZ_ASSERT(convertUnboxedGroups.empty());
622
623 *holder = nullptr;
624 const ICEntry& entry = icEntryFromPC(pc);
625
626 for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
627 if (stub->isGetProp_CallScripted() ||
628 stub->isGetProp_CallNative() ||
629 stub->isGetProp_CallNativeGlobal())
630 {
631 ICGetPropCallGetter* nstub = static_cast<ICGetPropCallGetter*>(stub);
632 bool isOwn = nstub->isOwnGetter();
633 if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups))
634 return false;
635
636 if (!*holder) {
637 *holder = nstub->holder();
638 *holderShape = nstub->holderShape();
639 *commonGetter = nstub->getter();
640 *globalShape = GlobalShapeForGetPropFunction(nstub);
641 *isOwnProperty = isOwn;
642 } else if (nstub->holderShape() != *holderShape ||
643 GlobalShapeForGetPropFunction(nstub) != *globalShape ||
644 isOwn != *isOwnProperty)
645 {
646 return false;
647 } else {
648 MOZ_ASSERT(*commonGetter == nstub->getter());
649 }
650 } else if (stub->isGetProp_Fallback()) {
651 // If we have an unoptimizable access, don't try to optimize.
652 if (stub->toGetProp_Fallback()->hadUnoptimizableAccess())
653 return false;
654 } else if (stub->isGetName_Fallback()) {
655 if (stub->toGetName_Fallback()->hadUnoptimizableAccess())
656 return false;
657 } else {
658 return false;
659 }
660 }
661
662 if (!*holder)
663 return false;
664
665 MOZ_ASSERT(*isOwnProperty == (receivers.empty() && convertUnboxedGroups.empty()));
666 return true;
667 }
668
669 bool
commonSetPropFunction(jsbytecode * pc,JSObject ** holder,Shape ** holderShape,JSFunction ** commonSetter,bool * isOwnProperty,ReceiverVector & receivers,ObjectGroupVector & convertUnboxedGroups)670 BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
671 JSFunction** commonSetter, bool* isOwnProperty,
672 ReceiverVector& receivers,
673 ObjectGroupVector& convertUnboxedGroups)
674 {
675 if (!hasBaselineScript())
676 return false;
677
678 MOZ_ASSERT(receivers.empty());
679 MOZ_ASSERT(convertUnboxedGroups.empty());
680
681 *holder = nullptr;
682 const ICEntry& entry = icEntryFromPC(pc);
683
684 for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
685 if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
686 ICSetPropCallSetter* nstub = static_cast<ICSetPropCallSetter*>(stub);
687 bool isOwn = nstub->isOwnSetter();
688 if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups))
689 return false;
690
691 if (!*holder) {
692 *holder = nstub->holder();
693 *holderShape = nstub->holderShape();
694 *commonSetter = nstub->setter();
695 *isOwnProperty = isOwn;
696 } else if (nstub->holderShape() != *holderShape || isOwn != *isOwnProperty) {
697 return false;
698 } else {
699 MOZ_ASSERT(*commonSetter == nstub->setter());
700 }
701 } else if (!stub->isSetProp_Fallback() ||
702 stub->toSetProp_Fallback()->hadUnoptimizableAccess())
703 {
704 // We have an unoptimizable access, so don't try to optimize.
705 return false;
706 }
707 }
708
709 if (!*holder)
710 return false;
711
712 return true;
713 }
714
715 MIRType
expectedPropertyAccessInputType(jsbytecode * pc)716 BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
717 {
718 if (!hasBaselineScript())
719 return MIRType_Value;
720
721 const ICEntry& entry = icEntryFromPC(pc);
722 MIRType type = MIRType_None;
723
724 for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
725 MIRType stubType;
726 switch (stub->kind()) {
727 case ICStub::GetProp_Fallback:
728 if (stub->toGetProp_Fallback()->hadUnoptimizableAccess())
729 return MIRType_Value;
730 continue;
731
732 case ICStub::GetElem_Fallback:
733 if (stub->toGetElem_Fallback()->hadUnoptimizableAccess())
734 return MIRType_Value;
735 continue;
736
737 case ICStub::GetProp_Generic:
738 return MIRType_Value;
739
740 case ICStub::GetProp_ArgumentsLength:
741 case ICStub::GetElem_Arguments:
742 // Either an object or magic arguments.
743 return MIRType_Value;
744
745 case ICStub::GetProp_ArrayLength:
746 case ICStub::GetProp_UnboxedArrayLength:
747 case ICStub::GetProp_Native:
748 case ICStub::GetProp_NativeDoesNotExist:
749 case ICStub::GetProp_NativePrototype:
750 case ICStub::GetProp_Unboxed:
751 case ICStub::GetProp_TypedObject:
752 case ICStub::GetProp_CallScripted:
753 case ICStub::GetProp_CallNative:
754 case ICStub::GetProp_CallDOMProxyNative:
755 case ICStub::GetProp_CallDOMProxyWithGenerationNative:
756 case ICStub::GetProp_DOMProxyShadowed:
757 case ICStub::GetProp_ModuleNamespace:
758 case ICStub::GetElem_NativeSlotName:
759 case ICStub::GetElem_NativeSlotSymbol:
760 case ICStub::GetElem_NativePrototypeSlotName:
761 case ICStub::GetElem_NativePrototypeSlotSymbol:
762 case ICStub::GetElem_NativePrototypeCallNativeName:
763 case ICStub::GetElem_NativePrototypeCallNativeSymbol:
764 case ICStub::GetElem_NativePrototypeCallScriptedName:
765 case ICStub::GetElem_NativePrototypeCallScriptedSymbol:
766 case ICStub::GetElem_UnboxedPropertyName:
767 case ICStub::GetElem_String:
768 case ICStub::GetElem_Dense:
769 case ICStub::GetElem_TypedArray:
770 case ICStub::GetElem_UnboxedArray:
771 stubType = MIRType_Object;
772 break;
773
774 case ICStub::GetProp_Primitive:
775 stubType = MIRTypeFromValueType(stub->toGetProp_Primitive()->primitiveType());
776 break;
777
778 case ICStub::GetProp_StringLength:
779 stubType = MIRType_String;
780 break;
781
782 default:
783 MOZ_CRASH("Unexpected stub");
784 }
785
786 if (type != MIRType_None) {
787 if (type != stubType)
788 return MIRType_Value;
789 } else {
790 type = stubType;
791 }
792 }
793
794 return (type == MIRType_None) ? MIRType_Value : type;
795 }
796
797 bool
instanceOfData(jsbytecode * pc,Shape ** shape,uint32_t * slot,JSObject ** prototypeObject)798 BaselineInspector::instanceOfData(jsbytecode* pc, Shape** shape, uint32_t* slot,
799 JSObject** prototypeObject)
800 {
801 MOZ_ASSERT(*pc == JSOP_INSTANCEOF);
802
803 if (!hasBaselineScript())
804 return false;
805
806 const ICEntry& entry = icEntryFromPC(pc);
807
808 ICStub* stub = entry.firstStub();
809 if (!stub->isInstanceOf_Function() ||
810 !stub->next()->isInstanceOf_Fallback() ||
811 stub->next()->toInstanceOf_Fallback()->hadUnoptimizableAccess())
812 {
813 return false;
814 }
815
816 ICInstanceOf_Function* optStub = stub->toInstanceOf_Function();
817 *shape = optStub->shape();
818 *prototypeObject = optStub->prototypeObject();
819 *slot = optStub->slot();
820
821 if (IsInsideNursery(*prototypeObject))
822 return false;
823
824 return true;
825 }
826