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