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 #include "jit/CacheIRCompiler.h"
13 
14 #include "vm/EnvironmentObject-inl.h"
15 #include "vm/JSScript-inl.h"
16 #include "vm/ObjectGroup-inl.h"
17 #include "vm/ReceiverGuard-inl.h"
18 
19 using namespace js;
20 using namespace js::jit;
21 
22 using mozilla::DebugOnly;
23 
sawOOBDenseWrite() const24 bool SetElemICInspector::sawOOBDenseWrite() const {
25   if (!icEntry_) return false;
26 
27   // Check for a write hole bit on the SetElem_Fallback stub.
28   ICStub* stub = icEntry_->fallbackStub();
29   if (stub->isSetElem_Fallback())
30     return stub->toSetElem_Fallback()->hasDenseAdd();
31 
32   return false;
33 }
34 
sawOOBTypedArrayWrite() const35 bool SetElemICInspector::sawOOBTypedArrayWrite() const {
36   if (!icEntry_) return false;
37 
38   ICStub* stub = icEntry_->fallbackStub();
39   if (stub->isSetElem_Fallback())
40     return stub->toSetElem_Fallback()->hasTypedArrayOOB();
41 
42   return false;
43 }
44 
45 template <typename S, typename T>
VectorAppendNoDuplicate(S & list,T value)46 static bool VectorAppendNoDuplicate(S& list, T value) {
47   for (size_t i = 0; i < list.length(); i++) {
48     if (list[i] == value) return true;
49   }
50   return list.append(value);
51 }
52 
AddReceiver(const ReceiverGuard & receiver,BaselineInspector::ReceiverVector & receivers,BaselineInspector::ObjectGroupVector & convertUnboxedGroups)53 static bool AddReceiver(
54     const ReceiverGuard& receiver, BaselineInspector::ReceiverVector& receivers,
55     BaselineInspector::ObjectGroupVector& convertUnboxedGroups) {
56   if (receiver.group && receiver.group->maybeUnboxedLayout()) {
57     if (receiver.group->unboxedLayout().nativeGroup())
58       return VectorAppendNoDuplicate(convertUnboxedGroups, receiver.group);
59   }
60   return VectorAppendNoDuplicate(receivers, receiver);
61 }
62 
GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored * stub,ReceiverGuard * receiver)63 static bool GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub,
64                                                 ReceiverGuard* receiver) {
65   // We match either:
66   //
67   //   GuardIsObject 0
68   //   GuardShape 0
69   //   LoadFixedSlotResult 0 or LoadDynamicSlotResult 0
70   //
71   // or
72   //
73   //   GuardIsObject 0
74   //   GuardGroup 0
75   //   1: GuardAndLoadUnboxedExpando 0
76   //   GuardShape 1
77   //   LoadFixedSlotResult 1 or LoadDynamicSlotResult 1
78 
79   *receiver = ReceiverGuard();
80   CacheIRReader reader(stub->stubInfo());
81 
82   ObjOperandId objId = ObjOperandId(0);
83   if (!reader.matchOp(CacheOp::GuardIsObject, objId)) return false;
84 
85   if (reader.matchOp(CacheOp::GuardGroup, objId)) {
86     receiver->group =
87         stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
88 
89     if (!reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId))
90       return false;
91     objId = reader.objOperandId();
92   }
93 
94   if (reader.matchOp(CacheOp::GuardShape, objId)) {
95     receiver->shape =
96         stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
97     return reader.matchOpEither(CacheOp::LoadFixedSlotResult,
98                                 CacheOp::LoadDynamicSlotResult);
99   }
100 
101   return false;
102 }
103 
GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Monitored * stub,ReceiverGuard * receiver)104 static bool GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Monitored* stub,
105                                                  ReceiverGuard* receiver) {
106   // We match:
107   //
108   //   GuardIsObject 0
109   //   GuardGroup 0
110   //   LoadUnboxedPropertyResult 0 ..
111 
112   *receiver = ReceiverGuard();
113   CacheIRReader reader(stub->stubInfo());
114 
115   ObjOperandId objId = ObjOperandId(0);
116   if (!reader.matchOp(CacheOp::GuardIsObject, objId)) return false;
117 
118   if (!reader.matchOp(CacheOp::GuardGroup, objId)) return false;
119   receiver->group =
120       stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
121 
122   return reader.matchOp(CacheOp::LoadUnboxedPropertyResult, objId);
123 }
124 
GetCacheIRReceiverForNativeSetSlot(ICCacheIR_Updated * stub,ReceiverGuard * receiver)125 static bool GetCacheIRReceiverForNativeSetSlot(ICCacheIR_Updated* stub,
126                                                ReceiverGuard* receiver) {
127   // We match either:
128   //
129   //   GuardIsObject 0
130   //   GuardGroup 0
131   //   GuardShape 0
132   //   StoreFixedSlot 0 or StoreDynamicSlot 0
133   //
134   // or
135   //
136   //   GuardIsObject 0
137   //   GuardGroup 0
138   //   1: GuardAndLoadUnboxedExpando 0
139   //   GuardShape 1
140   //   StoreFixedSlot 1 or StoreDynamicSlot 1
141 
142   *receiver = ReceiverGuard();
143   CacheIRReader reader(stub->stubInfo());
144 
145   ObjOperandId objId = ObjOperandId(0);
146   if (!reader.matchOp(CacheOp::GuardIsObject, objId)) return false;
147 
148   if (!reader.matchOp(CacheOp::GuardGroup, objId)) return false;
149   ObjectGroup* group =
150       stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
151 
152   if (reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId))
153     objId = reader.objOperandId();
154 
155   if (!reader.matchOp(CacheOp::GuardShape, objId)) return false;
156   Shape* shape =
157       stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
158 
159   if (!reader.matchOpEither(CacheOp::StoreFixedSlot, CacheOp::StoreDynamicSlot))
160     return false;
161 
162   *receiver = ReceiverGuard(group, shape);
163   return true;
164 }
165 
GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Updated * stub,ReceiverGuard * receiver)166 static bool GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Updated* stub,
167                                                  ReceiverGuard* receiver) {
168   // We match:
169   //
170   //   GuardIsObject 0
171   //   GuardGroup 0
172   //   GuardType 1 type | GuardIsObjectOrNull 1
173   //   StoreUnboxedProperty 0
174 
175   *receiver = ReceiverGuard();
176   CacheIRReader reader(stub->stubInfo());
177 
178   ObjOperandId objId = ObjOperandId(0);
179   ValOperandId rhsId = ValOperandId(1);
180   if (!reader.matchOp(CacheOp::GuardIsObject, objId)) return false;
181 
182   if (!reader.matchOp(CacheOp::GuardGroup, objId)) return false;
183   ObjectGroup* group =
184       stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
185 
186   if (reader.matchOp(CacheOp::GuardType, rhsId)) {
187     reader.valueType();  // Skip.
188   } else {
189     if (!reader.matchOp(CacheOp::GuardIsObjectOrNull, rhsId)) return false;
190   }
191 
192   if (!reader.matchOp(CacheOp::StoreUnboxedProperty)) return false;
193 
194   *receiver = ReceiverGuard(group, nullptr);
195   return true;
196 }
197 
maybeInfoForPropertyOp(jsbytecode * pc,ReceiverVector & receivers,ObjectGroupVector & convertUnboxedGroups)198 bool BaselineInspector::maybeInfoForPropertyOp(
199     jsbytecode* pc, ReceiverVector& receivers,
200     ObjectGroupVector& convertUnboxedGroups) {
201   // Return a list of the receivers seen by the baseline IC for the current
202   // op. Empty lists indicate no receivers are known, or there was an
203   // uncacheable access. convertUnboxedGroups is used for unboxed object
204   // groups which have been seen, but have had instances converted to native
205   // objects and should be eagerly converted by Ion.
206   MOZ_ASSERT(receivers.empty());
207   MOZ_ASSERT(convertUnboxedGroups.empty());
208 
209   if (!hasBaselineScript()) return true;
210 
211   MOZ_ASSERT(isValidPC(pc));
212   const ICEntry& entry = icEntryFromPC(pc);
213 
214   ICStub* stub = entry.firstStub();
215   while (stub->next()) {
216     ReceiverGuard receiver;
217     if (stub->isCacheIR_Monitored()) {
218       if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(),
219                                                &receiver) &&
220           !GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Monitored(),
221                                                 &receiver)) {
222         receivers.clear();
223         return true;
224       }
225     } else if (stub->isCacheIR_Updated()) {
226       if (!GetCacheIRReceiverForNativeSetSlot(stub->toCacheIR_Updated(),
227                                               &receiver) &&
228           !GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Updated(),
229                                                 &receiver)) {
230         receivers.clear();
231         return true;
232       }
233     } else {
234       receivers.clear();
235       return true;
236     }
237 
238     if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) return false;
239 
240     stub = stub->next();
241   }
242 
243   if (stub->isGetProp_Fallback()) {
244     if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) receivers.clear();
245   } else {
246     if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) receivers.clear();
247   }
248 
249   // Don't inline if there are more than 5 receivers.
250   if (receivers.length() > 5) receivers.clear();
251 
252   return true;
253 }
254 
monomorphicStub(jsbytecode * pc)255 ICStub* BaselineInspector::monomorphicStub(jsbytecode* pc) {
256   if (!hasBaselineScript()) return nullptr;
257 
258   // IonBuilder::analyzeNewLoopTypes may call this (via expectedResultType
259   // below) on code that's unreachable, according to BytecodeAnalysis. Use
260   // maybeICEntryFromPC to handle this.
261   const ICEntry* entry = maybeICEntryFromPC(pc);
262   if (!entry) return nullptr;
263 
264   ICStub* stub = entry->firstStub();
265   ICStub* next = stub->next();
266 
267   if (!next || !next->isFallback()) return nullptr;
268 
269   return stub;
270 }
271 
dimorphicStub(jsbytecode * pc,ICStub ** pfirst,ICStub ** psecond)272 bool BaselineInspector::dimorphicStub(jsbytecode* pc, ICStub** pfirst,
273                                       ICStub** psecond) {
274   if (!hasBaselineScript()) return false;
275 
276   const ICEntry& entry = icEntryFromPC(pc);
277 
278   ICStub* stub = entry.firstStub();
279   ICStub* next = stub->next();
280   ICStub* after = next ? next->next() : nullptr;
281 
282   if (!after || !after->isFallback()) return false;
283 
284   *pfirst = stub;
285   *psecond = next;
286   return true;
287 }
288 
expectedResultType(jsbytecode * pc)289 MIRType BaselineInspector::expectedResultType(jsbytecode* pc) {
290   // Look at the IC entries for this op to guess what type it will produce,
291   // returning MIRType::None otherwise. Note that IonBuilder may call this
292   // for bytecode ops that are unreachable and don't have a Baseline IC, see
293   // comment in monomorphicStub.
294 
295   ICStub* stub = monomorphicStub(pc);
296   if (!stub) return MIRType::None;
297 
298   switch (stub->kind()) {
299     case ICStub::BinaryArith_Int32:
300       if (stub->toBinaryArith_Int32()->allowDouble()) return MIRType::Double;
301       return MIRType::Int32;
302     case ICStub::BinaryArith_BooleanWithInt32:
303     case ICStub::UnaryArith_Int32:
304     case ICStub::BinaryArith_DoubleWithInt32:
305       return MIRType::Int32;
306     case ICStub::BinaryArith_Double:
307     case ICStub::UnaryArith_Double:
308       return MIRType::Double;
309     case ICStub::BinaryArith_StringConcat:
310     case ICStub::BinaryArith_StringObjectConcat:
311       return MIRType::String;
312     default:
313       return MIRType::None;
314   }
315 }
316 
317 // Whether a baseline stub kind is suitable for a double comparison that
318 // converts its operands to doubles.
CanUseDoubleCompare(ICStub::Kind kind)319 static bool CanUseDoubleCompare(ICStub::Kind kind) {
320   return kind == ICStub::Compare_Double ||
321          kind == ICStub::Compare_NumberWithUndefined;
322 }
323 
324 // Whether a baseline stub kind is suitable for an int32 comparison that
325 // converts its operands to int32.
CanUseInt32Compare(ICStub::Kind kind)326 static bool CanUseInt32Compare(ICStub::Kind kind) {
327   return kind == ICStub::Compare_Int32 ||
328          kind == ICStub::Compare_Int32WithBoolean;
329 }
330 
expectedCompareType(jsbytecode * pc)331 MCompare::CompareType BaselineInspector::expectedCompareType(jsbytecode* pc) {
332   ICStub* first = monomorphicStub(pc);
333   ICStub* second = nullptr;
334   if (!first && !dimorphicStub(pc, &first, &second))
335     return MCompare::Compare_Unknown;
336 
337   if (ICStub* fallback = second ? second->next() : first->next()) {
338     MOZ_ASSERT(fallback->isFallback());
339     if (fallback->toCompare_Fallback()->hadUnoptimizableAccess())
340       return MCompare::Compare_Unknown;
341   }
342 
343   if (CanUseInt32Compare(first->kind()) &&
344       (!second || CanUseInt32Compare(second->kind()))) {
345     ICCompare_Int32WithBoolean* coerce =
346         first->isCompare_Int32WithBoolean()
347             ? first->toCompare_Int32WithBoolean()
348             : ((second && second->isCompare_Int32WithBoolean())
349                    ? second->toCompare_Int32WithBoolean()
350                    : nullptr);
351     if (coerce) {
352       return coerce->lhsIsInt32() ? MCompare::Compare_Int32MaybeCoerceRHS
353                                   : MCompare::Compare_Int32MaybeCoerceLHS;
354     }
355     return MCompare::Compare_Int32;
356   }
357 
358   if (CanUseDoubleCompare(first->kind()) &&
359       (!second || CanUseDoubleCompare(second->kind()))) {
360     ICCompare_NumberWithUndefined* coerce =
361         first->isCompare_NumberWithUndefined()
362             ? first->toCompare_NumberWithUndefined()
363             : (second && second->isCompare_NumberWithUndefined())
364                   ? second->toCompare_NumberWithUndefined()
365                   : nullptr;
366     if (coerce) {
367       return coerce->lhsIsUndefined() ? MCompare::Compare_DoubleMaybeCoerceLHS
368                                       : MCompare::Compare_DoubleMaybeCoerceRHS;
369     }
370     return MCompare::Compare_Double;
371   }
372 
373   return MCompare::Compare_Unknown;
374 }
375 
TryToSpecializeBinaryArithOp(ICStub ** stubs,uint32_t nstubs,MIRType * result)376 static bool TryToSpecializeBinaryArithOp(ICStub** stubs, uint32_t nstubs,
377                                          MIRType* result) {
378   DebugOnly<bool> sawInt32 = false;
379   bool sawDouble = false;
380   bool sawOther = false;
381 
382   for (uint32_t i = 0; i < nstubs; i++) {
383     switch (stubs[i]->kind()) {
384       case ICStub::BinaryArith_Int32:
385         sawInt32 = true;
386         break;
387       case ICStub::BinaryArith_BooleanWithInt32:
388         sawInt32 = true;
389         break;
390       case ICStub::BinaryArith_Double:
391         sawDouble = true;
392         break;
393       case ICStub::BinaryArith_DoubleWithInt32:
394         sawDouble = true;
395         break;
396       default:
397         sawOther = true;
398         break;
399     }
400   }
401 
402   if (sawOther) return false;
403 
404   if (sawDouble) {
405     *result = MIRType::Double;
406     return true;
407   }
408 
409   MOZ_ASSERT(sawInt32);
410   *result = MIRType::Int32;
411   return true;
412 }
413 
expectedBinaryArithSpecialization(jsbytecode * pc)414 MIRType BaselineInspector::expectedBinaryArithSpecialization(jsbytecode* pc) {
415   if (!hasBaselineScript()) return MIRType::None;
416 
417   MIRType result;
418   ICStub* stubs[2];
419 
420   const ICEntry& entry = icEntryFromPC(pc);
421   ICStub* stub = entry.fallbackStub();
422   if (stub->isBinaryArith_Fallback() &&
423       stub->toBinaryArith_Fallback()->hadUnoptimizableOperands()) {
424     return MIRType::None;
425   }
426 
427   stubs[0] = monomorphicStub(pc);
428   if (stubs[0]) {
429     if (TryToSpecializeBinaryArithOp(stubs, 1, &result)) return result;
430   }
431 
432   if (dimorphicStub(pc, &stubs[0], &stubs[1])) {
433     if (TryToSpecializeBinaryArithOp(stubs, 2, &result)) return result;
434   }
435 
436   return MIRType::None;
437 }
438 
hasSeenNegativeIndexGetElement(jsbytecode * pc)439 bool BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode* pc) {
440   if (!hasBaselineScript()) return false;
441 
442   const ICEntry& entry = icEntryFromPC(pc);
443   ICStub* stub = entry.fallbackStub();
444 
445   if (stub->isGetElem_Fallback())
446     return stub->toGetElem_Fallback()->hasNegativeIndex();
447   return false;
448 }
449 
hasSeenAccessedGetter(jsbytecode * pc)450 bool BaselineInspector::hasSeenAccessedGetter(jsbytecode* pc) {
451   if (!hasBaselineScript()) return false;
452 
453   const ICEntry& entry = icEntryFromPC(pc);
454   ICStub* stub = entry.fallbackStub();
455 
456   if (stub->isGetProp_Fallback())
457     return stub->toGetProp_Fallback()->hasAccessedGetter();
458   return false;
459 }
460 
hasSeenNonStringIterMore(jsbytecode * pc)461 bool BaselineInspector::hasSeenNonStringIterMore(jsbytecode* pc) {
462   MOZ_ASSERT(JSOp(*pc) == JSOP_MOREITER);
463 
464   if (!hasBaselineScript()) return false;
465 
466   const ICEntry& entry = icEntryFromPC(pc);
467   ICStub* stub = entry.fallbackStub();
468 
469   return stub->toIteratorMore_Fallback()->hasNonStringResult();
470 }
471 
hasSeenDoubleResult(jsbytecode * pc)472 bool BaselineInspector::hasSeenDoubleResult(jsbytecode* pc) {
473   if (!hasBaselineScript()) return false;
474 
475   const ICEntry& entry = icEntryFromPC(pc);
476   ICStub* stub = entry.fallbackStub();
477 
478   MOZ_ASSERT(stub->isUnaryArith_Fallback() || stub->isBinaryArith_Fallback());
479 
480   if (stub->isUnaryArith_Fallback())
481     return stub->toUnaryArith_Fallback()->sawDoubleResult();
482 
483   return stub->toBinaryArith_Fallback()->sawDoubleResult();
484 }
485 
getTemplateObject(jsbytecode * pc)486 JSObject* BaselineInspector::getTemplateObject(jsbytecode* pc) {
487   if (!hasBaselineScript()) return nullptr;
488 
489   const ICEntry& entry = icEntryFromPC(pc);
490   for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
491     switch (stub->kind()) {
492       case ICStub::NewArray_Fallback:
493         return stub->toNewArray_Fallback()->templateObject();
494       case ICStub::NewObject_Fallback:
495         return stub->toNewObject_Fallback()->templateObject();
496       case ICStub::Rest_Fallback:
497         return stub->toRest_Fallback()->templateObject();
498       case ICStub::Call_Scripted:
499         if (JSObject* obj = stub->toCall_Scripted()->templateObject())
500           return obj;
501         break;
502       default:
503         break;
504     }
505   }
506 
507   return nullptr;
508 }
509 
getTemplateObjectGroup(jsbytecode * pc)510 ObjectGroup* BaselineInspector::getTemplateObjectGroup(jsbytecode* pc) {
511   if (!hasBaselineScript()) return nullptr;
512 
513   const ICEntry& entry = icEntryFromPC(pc);
514   for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
515     switch (stub->kind()) {
516       case ICStub::NewArray_Fallback:
517         return stub->toNewArray_Fallback()->templateGroup();
518       default:
519         break;
520     }
521   }
522 
523   return nullptr;
524 }
525 
getSingleCallee(jsbytecode * pc)526 JSFunction* BaselineInspector::getSingleCallee(jsbytecode* pc) {
527   MOZ_ASSERT(*pc == JSOP_NEW);
528 
529   if (!hasBaselineScript()) return nullptr;
530 
531   const ICEntry& entry = icEntryFromPC(pc);
532   ICStub* stub = entry.firstStub();
533 
534   if (entry.fallbackStub()->toCall_Fallback()->hadUnoptimizableCall())
535     return nullptr;
536 
537   if (!stub->isCall_Scripted() || stub->next() != entry.fallbackStub())
538     return nullptr;
539 
540   return stub->toCall_Scripted()->callee();
541 }
542 
getTemplateObjectForNative(jsbytecode * pc,Native native)543 JSObject* BaselineInspector::getTemplateObjectForNative(jsbytecode* pc,
544                                                         Native native) {
545   if (!hasBaselineScript()) return nullptr;
546 
547   const ICEntry& entry = icEntryFromPC(pc);
548   for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
549     if (stub->isCall_Native() &&
550         stub->toCall_Native()->callee()->native() == native)
551       return stub->toCall_Native()->templateObject();
552   }
553 
554   return nullptr;
555 }
556 
isOptimizableConstStringSplit(jsbytecode * pc,JSString ** strOut,JSString ** sepOut,ArrayObject ** objOut)557 bool BaselineInspector::isOptimizableConstStringSplit(jsbytecode* pc,
558                                                       JSString** strOut,
559                                                       JSString** sepOut,
560                                                       ArrayObject** objOut) {
561   if (!hasBaselineScript()) return false;
562 
563   const ICEntry& entry = icEntryFromPC(pc);
564 
565   // If ConstStringSplit stub is attached, must have only one stub attached.
566   if (entry.fallbackStub()->numOptimizedStubs() != 1) return false;
567 
568   ICStub* stub = entry.firstStub();
569   if (stub->kind() != ICStub::Call_ConstStringSplit) return false;
570 
571   *strOut = stub->toCall_ConstStringSplit()->expectedStr();
572   *sepOut = stub->toCall_ConstStringSplit()->expectedSep();
573   *objOut = stub->toCall_ConstStringSplit()->templateObject();
574   return true;
575 }
576 
getTemplateObjectForClassHook(jsbytecode * pc,const Class * clasp)577 JSObject* BaselineInspector::getTemplateObjectForClassHook(jsbytecode* pc,
578                                                            const Class* clasp) {
579   if (!hasBaselineScript()) return nullptr;
580 
581   const ICEntry& entry = icEntryFromPC(pc);
582   for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
583     if (stub->isCall_ClassHook() && stub->toCall_ClassHook()->clasp() == clasp)
584       return stub->toCall_ClassHook()->templateObject();
585   }
586 
587   return nullptr;
588 }
589 
getTemplateObjectForSimdCtor(jsbytecode * pc,SimdType simdType)590 JSObject* BaselineInspector::getTemplateObjectForSimdCtor(jsbytecode* pc,
591                                                           SimdType simdType) {
592   if (!hasBaselineScript()) return nullptr;
593 
594   const ICEntry& entry = icEntryFromPC(pc);
595   for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
596     if (stub->isCall_ClassHook() &&
597         stub->toCall_ClassHook()->clasp() == &SimdTypeDescr::class_) {
598       JSObject* templateObj = stub->toCall_ClassHook()->templateObject();
599       InlineTypedObject& typedObj = templateObj->as<InlineTypedObject>();
600       if (typedObj.typeDescr().as<SimdTypeDescr>().type() == simdType)
601         return templateObj;
602     }
603   }
604 
605   return nullptr;
606 }
607 
templateNamedLambdaObject()608 LexicalEnvironmentObject* BaselineInspector::templateNamedLambdaObject() {
609   if (!hasBaselineScript()) return nullptr;
610 
611   JSObject* res = baselineScript()->templateEnvironment();
612   if (script->bodyScope()->hasEnvironment()) res = res->enclosingEnvironment();
613   MOZ_ASSERT(res);
614 
615   return &res->as<LexicalEnvironmentObject>();
616 }
617 
templateCallObject()618 CallObject* BaselineInspector::templateCallObject() {
619   if (!hasBaselineScript()) return nullptr;
620 
621   JSObject* res = baselineScript()->templateEnvironment();
622   MOZ_ASSERT(res);
623 
624   return &res->as<CallObject>();
625 }
626 
MatchCacheIRReceiverGuard(CacheIRReader & reader,ICStub * stub,const CacheIRStubInfo * stubInfo,ObjOperandId objId,ReceiverGuard * receiver)627 static bool MatchCacheIRReceiverGuard(CacheIRReader& reader, ICStub* stub,
628                                       const CacheIRStubInfo* stubInfo,
629                                       ObjOperandId objId,
630                                       ReceiverGuard* receiver) {
631   // This matches the CacheIR emitted in TestMatchingReceiver.
632   //
633   // Either:
634   //
635   //   GuardShape objId
636   //
637   // or:
638   //
639   //   GuardGroup objId
640   //   [GuardNoUnboxedExpando objId]
641   //
642   // or:
643   //
644   //   GuardGroup objId
645   //   expandoId: GuardAndLoadUnboxedExpando
646   //   GuardShape expandoId
647 
648   *receiver = ReceiverGuard();
649 
650   if (reader.matchOp(CacheOp::GuardShape, objId)) {
651     // The first case.
652     receiver->shape = stubInfo->getStubField<Shape*>(stub, reader.stubOffset());
653     return true;
654   }
655 
656   if (!reader.matchOp(CacheOp::GuardGroup, objId)) return false;
657   receiver->group =
658       stubInfo->getStubField<ObjectGroup*>(stub, reader.stubOffset());
659 
660   if (!reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId)) {
661     // Second case, just a group guard.
662     reader.matchOp(CacheOp::GuardNoUnboxedExpando, objId);
663     return true;
664   }
665 
666   // Third case.
667   ObjOperandId expandoId = reader.objOperandId();
668   if (!reader.matchOp(CacheOp::GuardShape, expandoId)) return false;
669 
670   receiver->shape = stubInfo->getStubField<Shape*>(stub, reader.stubOffset());
671   return true;
672 }
673 
AddCacheIRGlobalGetter(ICCacheIR_Monitored * stub,bool innerized,JSObject ** holder_,Shape ** holderShape_,JSFunction ** commonGetter,Shape ** globalShape_,bool * isOwnProperty,BaselineInspector::ReceiverVector & receivers,BaselineInspector::ObjectGroupVector & convertUnboxedGroups,JSScript * script)674 static bool AddCacheIRGlobalGetter(
675     ICCacheIR_Monitored* stub, bool innerized, JSObject** holder_,
676     Shape** holderShape_, JSFunction** commonGetter, Shape** globalShape_,
677     bool* isOwnProperty, BaselineInspector::ReceiverVector& receivers,
678     BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
679     JSScript* script) {
680   // We are matching on the IR generated by tryAttachGlobalNameGetter:
681   //
682   //   GuardShape objId
683   //   globalId = LoadEnclosingEnvironment objId
684   //   GuardShape globalId
685   //   <holderId = LoadObject <holder>>
686   //   <GuardShape holderId>
687   //   CallNativeGetterResult globalId
688 
689   CacheIRReader reader(stub->stubInfo());
690 
691   ObjOperandId objId = ObjOperandId(0);
692   if (!reader.matchOp(CacheOp::GuardShape, objId)) return false;
693   Shape* globalLexicalShape =
694       stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
695 
696   if (!reader.matchOp(CacheOp::LoadEnclosingEnvironment, objId)) return false;
697   ObjOperandId globalId = reader.objOperandId();
698 
699   if (!reader.matchOp(CacheOp::GuardShape, globalId)) return false;
700   Shape* globalShape =
701       stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
702   MOZ_ASSERT(globalShape->getObjectClass()->flags & JSCLASS_IS_GLOBAL);
703 
704   JSObject* holder = &script->global();
705   Shape* holderShape = globalShape;
706   if (reader.matchOp(CacheOp::LoadObject)) {
707     ObjOperandId holderId = reader.objOperandId();
708     holder = stub->stubInfo()
709                  ->getStubField<JSObject*>(stub, reader.stubOffset())
710                  .get();
711 
712     if (!reader.matchOp(CacheOp::GuardShape, holderId)) return false;
713     holderShape =
714         stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
715   }
716 
717   // This guard will always fail, try the next stub.
718   if (holder->as<NativeObject>().lastProperty() != holderShape) return true;
719 
720   if (!reader.matchOp(CacheOp::CallNativeGetterResult, globalId)) return false;
721   size_t offset = reader.stubOffset();
722   JSFunction* getter = &stub->stubInfo()
723                             ->getStubField<JSObject*>(stub, offset)
724                             ->as<JSFunction>();
725 
726   ReceiverGuard receiver;
727   receiver.shape = globalLexicalShape;
728   if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) return false;
729 
730   if (!*commonGetter) {
731     *holder_ = holder;
732     *holderShape_ = holderShape;
733     *commonGetter = getter;
734     *globalShape_ = globalShape;
735 
736     // This is always false, because the getters never live on the
737     // globalLexical.
738     *isOwnProperty = false;
739   } else if (*isOwnProperty || holderShape != *holderShape_ ||
740              globalShape != *globalShape_) {
741     return false;
742   } else {
743     MOZ_ASSERT(*commonGetter == getter);
744   }
745 
746   return true;
747 }
748 
AddCacheIRGetPropFunction(ICCacheIR_Monitored * stub,bool innerized,JSObject ** holder,Shape ** holderShape,JSFunction ** commonGetter,Shape ** globalShape,bool * isOwnProperty,BaselineInspector::ReceiverVector & receivers,BaselineInspector::ObjectGroupVector & convertUnboxedGroups,JSScript * script)749 static bool AddCacheIRGetPropFunction(
750     ICCacheIR_Monitored* stub, bool innerized, JSObject** holder,
751     Shape** holderShape, JSFunction** commonGetter, Shape** globalShape,
752     bool* isOwnProperty, BaselineInspector::ReceiverVector& receivers,
753     BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
754     JSScript* script) {
755   // We match either an own getter:
756   //
757   //   GuardIsObject objId
758   //   [..WindowProxy innerization..]
759   //   <GuardReceiver objId>
760   //   Call(Scripted|Native)GetterResult objId
761   //
762   // Or a getter on the prototype:
763   //
764   //   GuardIsObject objId
765   //   [..WindowProxy innerization..]
766   //   <GuardReceiver objId>
767   //   LoadObject holderId
768   //   GuardShape holderId
769   //   Call(Scripted|Native)GetterResult objId
770   //
771   // If |innerized| is true, we replaced a WindowProxy with the Window
772   // object and we're only interested in Baseline getter stubs that performed
773   // the same optimization. This means we expect the following ops for the
774   // [..WindowProxy innerization..] above:
775   //
776   //   GuardClass objId WindowProxy
777   //   objId = LoadObject <global>
778 
779   CacheIRReader reader(stub->stubInfo());
780 
781   ObjOperandId objId = ObjOperandId(0);
782   if (!reader.matchOp(CacheOp::GuardIsObject, objId)) {
783     return AddCacheIRGlobalGetter(stub, innerized, holder, holderShape,
784                                   commonGetter, globalShape, isOwnProperty,
785                                   receivers, convertUnboxedGroups, script);
786   }
787 
788   if (innerized) {
789     if (!reader.matchOp(CacheOp::GuardClass, objId) ||
790         reader.guardClassKind() != GuardClassKind::WindowProxy) {
791       return false;
792     }
793     if (!reader.matchOp(CacheOp::LoadObject)) return false;
794     objId = reader.objOperandId();
795     DebugOnly<JSObject*> obj =
796         stub->stubInfo()
797             ->getStubField<JSObject*>(stub, reader.stubOffset())
798             .get();
799     MOZ_ASSERT(obj->is<GlobalObject>());
800   }
801 
802   ReceiverGuard receiver;
803   if (!MatchCacheIRReceiverGuard(reader, stub, stub->stubInfo(), objId,
804                                  &receiver))
805     return false;
806 
807   if (reader.matchOp(CacheOp::CallScriptedGetterResult, objId) ||
808       reader.matchOp(CacheOp::CallNativeGetterResult, objId)) {
809     // This is an own property getter, the first case.
810     MOZ_ASSERT(receiver.shape);
811     MOZ_ASSERT(!receiver.group);
812 
813     size_t offset = reader.stubOffset();
814     JSFunction* getter = &stub->stubInfo()
815                               ->getStubField<JSObject*>(stub, offset)
816                               ->as<JSFunction>();
817 
818     if (*commonGetter &&
819         (!*isOwnProperty || *globalShape || *holderShape != receiver.shape))
820       return false;
821 
822     MOZ_ASSERT_IF(*commonGetter, *commonGetter == getter);
823     *holder = nullptr;
824     *holderShape = receiver.shape;
825     *commonGetter = getter;
826     *isOwnProperty = true;
827     return true;
828   }
829 
830   if (!reader.matchOp(CacheOp::LoadObject)) return false;
831   ObjOperandId holderId = reader.objOperandId();
832   JSObject* obj =
833       stub->stubInfo()->getStubField<JSObject*>(stub, reader.stubOffset());
834 
835   if (!reader.matchOp(CacheOp::GuardShape, holderId)) return false;
836   Shape* objShape =
837       stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
838 
839   if (!reader.matchOp(CacheOp::CallScriptedGetterResult, objId) &&
840       !reader.matchOp(CacheOp::CallNativeGetterResult, objId)) {
841     return false;
842   }
843 
844   // A getter on the prototype.
845   size_t offset = reader.stubOffset();
846   JSFunction* getter = &stub->stubInfo()
847                             ->getStubField<JSObject*>(stub, offset)
848                             ->as<JSFunction>();
849 
850   Shape* thisGlobalShape = nullptr;
851   if (getter->isNative() && receiver.shape &&
852       (receiver.shape->getObjectClass()->flags & JSCLASS_IS_GLOBAL)) {
853     thisGlobalShape = receiver.shape;
854   }
855 
856   if (*commonGetter && (*isOwnProperty || *globalShape != thisGlobalShape ||
857                         *holderShape != objShape)) {
858     return false;
859   }
860 
861   MOZ_ASSERT_IF(*commonGetter, *commonGetter == getter);
862 
863   if (obj->as<NativeObject>().lastProperty() != objShape) {
864     // Skip this stub as the shape is no longer correct.
865     return true;
866   }
867 
868   if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) return false;
869 
870   *holder = obj;
871   *holderShape = objShape;
872   *commonGetter = getter;
873   *isOwnProperty = false;
874   return true;
875 }
876 
commonGetPropFunction(jsbytecode * pc,bool innerized,JSObject ** holder,Shape ** holderShape,JSFunction ** commonGetter,Shape ** globalShape,bool * isOwnProperty,ReceiverVector & receivers,ObjectGroupVector & convertUnboxedGroups)877 bool BaselineInspector::commonGetPropFunction(
878     jsbytecode* pc, bool innerized, JSObject** holder, Shape** holderShape,
879     JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty,
880     ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups) {
881   if (!hasBaselineScript()) return false;
882 
883   MOZ_ASSERT(receivers.empty());
884   MOZ_ASSERT(convertUnboxedGroups.empty());
885 
886   *globalShape = nullptr;
887   *commonGetter = nullptr;
888   const ICEntry& entry = icEntryFromPC(pc);
889 
890   for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
891     if (stub->isCacheIR_Monitored()) {
892       if (!AddCacheIRGetPropFunction(stub->toCacheIR_Monitored(), innerized,
893                                      holder, holderShape, commonGetter,
894                                      globalShape, isOwnProperty, receivers,
895                                      convertUnboxedGroups, script)) {
896         return false;
897       }
898     } else if (stub->isGetProp_Fallback()) {
899       // If we have an unoptimizable access, don't try to optimize.
900       if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) return false;
901     } else if (stub->isGetName_Fallback()) {
902       if (stub->toGetName_Fallback()->hadUnoptimizableAccess()) return false;
903     } else {
904       return false;
905     }
906   }
907 
908   if (!*commonGetter) return false;
909 
910   MOZ_ASSERT(*isOwnProperty == !*holder);
911   MOZ_ASSERT(*isOwnProperty ==
912              (receivers.empty() && convertUnboxedGroups.empty()));
913   return true;
914 }
915 
GetMegamorphicGetterSetterFunction(ICStub * stub,const CacheIRStubInfo * stubInfo,bool isGetter)916 static JSFunction* GetMegamorphicGetterSetterFunction(
917     ICStub* stub, const CacheIRStubInfo* stubInfo, bool isGetter) {
918   // We match:
919   //
920   //   GuardIsObject objId
921   //   GuardHasGetterSetter objId propShape
922   //
923   // propShape has the getter/setter we're interested in.
924 
925   CacheIRReader reader(stubInfo);
926 
927   ObjOperandId objId = ObjOperandId(0);
928   if (!reader.matchOp(CacheOp::GuardIsObject, objId)) return nullptr;
929 
930   if (!reader.matchOp(CacheOp::GuardHasGetterSetter, objId)) return nullptr;
931   Shape* propShape = stubInfo->getStubField<Shape*>(stub, reader.stubOffset());
932 
933   JSObject* obj =
934       isGetter ? propShape->getterObject() : propShape->setterObject();
935   return &obj->as<JSFunction>();
936 }
937 
megamorphicGetterSetterFunction(jsbytecode * pc,bool isGetter,JSFunction ** getterOrSetter)938 bool BaselineInspector::megamorphicGetterSetterFunction(
939     jsbytecode* pc, bool isGetter, JSFunction** getterOrSetter) {
940   if (!hasBaselineScript()) return false;
941 
942   *getterOrSetter = nullptr;
943   const ICEntry& entry = icEntryFromPC(pc);
944 
945   for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
946     if (stub->isCacheIR_Monitored()) {
947       MOZ_ASSERT(isGetter);
948       JSFunction* getter = GetMegamorphicGetterSetterFunction(
949           stub, stub->toCacheIR_Monitored()->stubInfo(), isGetter);
950       if (!getter || (*getterOrSetter && *getterOrSetter != getter))
951         return false;
952       *getterOrSetter = getter;
953       continue;
954     }
955     if (stub->isCacheIR_Updated()) {
956       MOZ_ASSERT(!isGetter);
957       JSFunction* setter = GetMegamorphicGetterSetterFunction(
958           stub, stub->toCacheIR_Updated()->stubInfo(), isGetter);
959       if (!setter || (*getterOrSetter && *getterOrSetter != setter))
960         return false;
961       *getterOrSetter = setter;
962       continue;
963     }
964     if (stub->isGetProp_Fallback()) {
965       if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) return false;
966       if (stub->toGetProp_Fallback()->state().mode() !=
967           ICState::Mode::Megamorphic)
968         return false;
969       continue;
970     }
971     if (stub->isSetProp_Fallback()) {
972       if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) return false;
973       if (stub->toSetProp_Fallback()->state().mode() !=
974           ICState::Mode::Megamorphic)
975         return false;
976       continue;
977     }
978 
979     return false;
980   }
981 
982   if (!*getterOrSetter) return false;
983 
984   return true;
985 }
986 
AddCacheIRSetPropFunction(ICCacheIR_Updated * stub,JSObject ** holder,Shape ** holderShape,JSFunction ** commonSetter,bool * isOwnProperty,BaselineInspector::ReceiverVector & receivers,BaselineInspector::ObjectGroupVector & convertUnboxedGroups)987 static bool AddCacheIRSetPropFunction(
988     ICCacheIR_Updated* stub, JSObject** holder, Shape** holderShape,
989     JSFunction** commonSetter, bool* isOwnProperty,
990     BaselineInspector::ReceiverVector& receivers,
991     BaselineInspector::ObjectGroupVector& convertUnboxedGroups) {
992   // We match either an own setter:
993   //
994   //   GuardIsObject objId
995   //   <GuardReceiver objId>
996   //   Call(Scripted|Native)Setter objId
997   //
998   // Or a setter on the prototype:
999   //
1000   //   GuardIsObject objId
1001   //   <GuardReceiver objId>
1002   //   LoadObject holderId
1003   //   GuardShape holderId
1004   //   Call(Scripted|Native)Setter objId
1005 
1006   CacheIRReader reader(stub->stubInfo());
1007 
1008   ObjOperandId objId = ObjOperandId(0);
1009   if (!reader.matchOp(CacheOp::GuardIsObject, objId)) return false;
1010 
1011   ReceiverGuard receiver;
1012   if (!MatchCacheIRReceiverGuard(reader, stub, stub->stubInfo(), objId,
1013                                  &receiver))
1014     return false;
1015 
1016   if (reader.matchOp(CacheOp::CallScriptedSetter, objId) ||
1017       reader.matchOp(CacheOp::CallNativeSetter, objId)) {
1018     // This is an own property setter, the first case.
1019     MOZ_ASSERT(receiver.shape);
1020     MOZ_ASSERT(!receiver.group);
1021 
1022     size_t offset = reader.stubOffset();
1023     JSFunction* setter = &stub->stubInfo()
1024                               ->getStubField<JSObject*>(stub, offset)
1025                               ->as<JSFunction>();
1026 
1027     if (*commonSetter && (!*isOwnProperty || *holderShape != receiver.shape))
1028       return false;
1029 
1030     MOZ_ASSERT_IF(*commonSetter, *commonSetter == setter);
1031     *holder = nullptr;
1032     *holderShape = receiver.shape;
1033     *commonSetter = setter;
1034     *isOwnProperty = true;
1035     return true;
1036   }
1037 
1038   if (!reader.matchOp(CacheOp::LoadObject)) return false;
1039   ObjOperandId holderId = reader.objOperandId();
1040   JSObject* obj =
1041       stub->stubInfo()->getStubField<JSObject*>(stub, reader.stubOffset());
1042 
1043   if (!reader.matchOp(CacheOp::GuardShape, holderId)) return false;
1044   Shape* objShape =
1045       stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
1046 
1047   if (!reader.matchOp(CacheOp::CallScriptedSetter, objId) &&
1048       !reader.matchOp(CacheOp::CallNativeSetter, objId)) {
1049     return false;
1050   }
1051 
1052   // A setter on the prototype.
1053   size_t offset = reader.stubOffset();
1054   JSFunction* setter = &stub->stubInfo()
1055                             ->getStubField<JSObject*>(stub, offset)
1056                             ->as<JSFunction>();
1057 
1058   if (*commonSetter && (*isOwnProperty || *holderShape != objShape))
1059     return false;
1060 
1061   MOZ_ASSERT_IF(*commonSetter, *commonSetter == setter);
1062 
1063   if (obj->as<NativeObject>().lastProperty() != objShape) {
1064     // Skip this stub as the shape is no longer correct.
1065     return true;
1066   }
1067 
1068   if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) return false;
1069 
1070   *holder = obj;
1071   *holderShape = objShape;
1072   *commonSetter = setter;
1073   *isOwnProperty = false;
1074   return true;
1075 }
1076 
commonSetPropFunction(jsbytecode * pc,JSObject ** holder,Shape ** holderShape,JSFunction ** commonSetter,bool * isOwnProperty,ReceiverVector & receivers,ObjectGroupVector & convertUnboxedGroups)1077 bool BaselineInspector::commonSetPropFunction(
1078     jsbytecode* pc, JSObject** holder, Shape** holderShape,
1079     JSFunction** commonSetter, bool* isOwnProperty, ReceiverVector& receivers,
1080     ObjectGroupVector& convertUnboxedGroups) {
1081   if (!hasBaselineScript()) return false;
1082 
1083   MOZ_ASSERT(receivers.empty());
1084   MOZ_ASSERT(convertUnboxedGroups.empty());
1085 
1086   *commonSetter = nullptr;
1087   const ICEntry& entry = icEntryFromPC(pc);
1088 
1089   for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
1090     if (stub->isCacheIR_Updated()) {
1091       if (!AddCacheIRSetPropFunction(stub->toCacheIR_Updated(), holder,
1092                                      holderShape, commonSetter, isOwnProperty,
1093                                      receivers, convertUnboxedGroups)) {
1094         return false;
1095       }
1096     } else if (!stub->isSetProp_Fallback() ||
1097                stub->toSetProp_Fallback()->hadUnoptimizableAccess()) {
1098       // We have an unoptimizable access, so don't try to optimize.
1099       return false;
1100     }
1101   }
1102 
1103   if (!*commonSetter) return false;
1104 
1105   MOZ_ASSERT(*isOwnProperty == !*holder);
1106   return true;
1107 }
1108 
GetCacheIRReceiverForProtoReadSlot(ICCacheIR_Monitored * stub,ReceiverGuard * receiver,JSObject ** holderResult)1109 static bool GetCacheIRReceiverForProtoReadSlot(ICCacheIR_Monitored* stub,
1110                                                ReceiverGuard* receiver,
1111                                                JSObject** holderResult) {
1112   // We match:
1113   //
1114   //   GuardIsObject 0
1115   //   <ReceiverGuard>
1116   //   1: LoadObject holder
1117   //   GuardShape 1
1118   //   LoadFixedSlotResult 1 or LoadDynamicSlotResult 1
1119 
1120   *receiver = ReceiverGuard();
1121   CacheIRReader reader(stub->stubInfo());
1122 
1123   ObjOperandId objId = ObjOperandId(0);
1124   if (!reader.matchOp(CacheOp::GuardIsObject, objId)) return false;
1125 
1126   if (!MatchCacheIRReceiverGuard(reader, stub, stub->stubInfo(), objId,
1127                                  receiver))
1128     return false;
1129 
1130   if (!reader.matchOp(CacheOp::LoadObject)) return false;
1131   ObjOperandId holderId = reader.objOperandId();
1132   JSObject* holder = stub->stubInfo()
1133                          ->getStubField<JSObject*>(stub, reader.stubOffset())
1134                          .get();
1135 
1136   if (!reader.matchOp(CacheOp::GuardShape, holderId)) return false;
1137   Shape* holderShape =
1138       stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
1139 
1140   if (!reader.matchOpEither(CacheOp::LoadFixedSlotResult,
1141                             CacheOp::LoadDynamicSlotResult))
1142     return false;
1143   if (reader.objOperandId() != holderId) return false;
1144 
1145   if (holder->maybeShape() != holderShape) return false;
1146   if (*holderResult && *holderResult != holder) return false;
1147 
1148   *holderResult = holder;
1149   return true;
1150 }
1151 
maybeInfoForProtoReadSlot(jsbytecode * pc,ReceiverVector & receivers,ObjectGroupVector & convertUnboxedGroups,JSObject ** holder)1152 bool BaselineInspector::maybeInfoForProtoReadSlot(
1153     jsbytecode* pc, ReceiverVector& receivers,
1154     ObjectGroupVector& convertUnboxedGroups, JSObject** holder) {
1155   // This is like maybeInfoForPropertyOp, but for when the property exists on
1156   // the prototype.
1157 
1158   MOZ_ASSERT(receivers.empty());
1159   MOZ_ASSERT(convertUnboxedGroups.empty());
1160   MOZ_ASSERT(!*holder);
1161 
1162   if (!hasBaselineScript()) return true;
1163 
1164   MOZ_ASSERT(isValidPC(pc));
1165   const ICEntry& entry = icEntryFromPC(pc);
1166 
1167   ICStub* stub = entry.firstStub();
1168   while (stub->next()) {
1169     ReceiverGuard receiver;
1170     if (stub->isCacheIR_Monitored()) {
1171       if (!GetCacheIRReceiverForProtoReadSlot(stub->toCacheIR_Monitored(),
1172                                               &receiver, holder)) {
1173         receivers.clear();
1174         return true;
1175       }
1176     } else {
1177       receivers.clear();
1178       return true;
1179     }
1180 
1181     if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) return false;
1182 
1183     stub = stub->next();
1184   }
1185 
1186   if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) receivers.clear();
1187 
1188   // Don't inline if there are more than 5 receivers.
1189   if (receivers.length() > 5) receivers.clear();
1190 
1191   MOZ_ASSERT_IF(!receivers.empty(), *holder);
1192   return true;
1193 }
1194 
GetCacheIRExpectedInputType(ICCacheIR_Monitored * stub)1195 static MIRType GetCacheIRExpectedInputType(ICCacheIR_Monitored* stub) {
1196   CacheIRReader reader(stub->stubInfo());
1197 
1198   if (reader.matchOp(CacheOp::GuardIsObject, ValOperandId(0)))
1199     return MIRType::Object;
1200   if (reader.matchOp(CacheOp::GuardIsString, ValOperandId(0)))
1201     return MIRType::String;
1202   if (reader.matchOp(CacheOp::GuardType, ValOperandId(0))) {
1203     JSValueType type = reader.valueType();
1204     return MIRTypeFromValueType(type);
1205   }
1206 
1207   MOZ_ASSERT_UNREACHABLE("Unexpected instruction");
1208   return MIRType::Value;
1209 }
1210 
expectedPropertyAccessInputType(jsbytecode * pc)1211 MIRType BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc) {
1212   if (!hasBaselineScript()) return MIRType::Value;
1213 
1214   const ICEntry& entry = icEntryFromPC(pc);
1215   MIRType type = MIRType::None;
1216 
1217   for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
1218     MIRType stubType;
1219     switch (stub->kind()) {
1220       case ICStub::GetProp_Fallback:
1221         if (stub->toGetProp_Fallback()->hadUnoptimizableAccess())
1222           return MIRType::Value;
1223         continue;
1224 
1225       case ICStub::GetElem_Fallback:
1226         if (stub->toGetElem_Fallback()->hadUnoptimizableAccess())
1227           return MIRType::Value;
1228         continue;
1229 
1230       case ICStub::CacheIR_Monitored:
1231         stubType = GetCacheIRExpectedInputType(stub->toCacheIR_Monitored());
1232         if (stubType == MIRType::Value) return MIRType::Value;
1233         break;
1234 
1235       default:
1236         MOZ_CRASH("Unexpected stub");
1237     }
1238 
1239     if (type != MIRType::None) {
1240       if (type != stubType) return MIRType::Value;
1241     } else {
1242       type = stubType;
1243     }
1244   }
1245 
1246   return (type == MIRType::None) ? MIRType::Value : type;
1247 }
1248 
instanceOfData(jsbytecode * pc,Shape ** shape,uint32_t * slot,JSObject ** prototypeObject)1249 bool BaselineInspector::instanceOfData(jsbytecode* pc, Shape** shape,
1250                                        uint32_t* slot,
1251                                        JSObject** prototypeObject) {
1252   MOZ_ASSERT(*pc == JSOP_INSTANCEOF);
1253   if (!hasBaselineScript()) return false;
1254 
1255   const ICEntry& entry = icEntryFromPC(pc);
1256   ICStub* firstStub = entry.firstStub();
1257 
1258   // Ensure singleton instanceof stub
1259   if (!firstStub->next() || !firstStub->isCacheIR_Regular() ||
1260       !firstStub->next()->isInstanceOf_Fallback() ||
1261       firstStub->next()->toInstanceOf_Fallback()->hadUnoptimizableAccess()) {
1262     return false;
1263   }
1264 
1265   ICCacheIR_Regular* stub = entry.firstStub()->toCacheIR_Regular();
1266   CacheIRReader reader(stub->stubInfo());
1267 
1268   ObjOperandId rhsId = ObjOperandId(1);
1269   ObjOperandId resId = ObjOperandId(2);
1270 
1271   if (!reader.matchOp(CacheOp::GuardIsObject, rhsId)) return false;
1272 
1273   if (!reader.matchOp(CacheOp::GuardShape, rhsId)) return false;
1274 
1275   *shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
1276 
1277   if (!reader.matchOp(CacheOp::LoadObject, resId)) return false;
1278 
1279   *prototypeObject = stub->stubInfo()
1280                          ->getStubField<JSObject*>(stub, reader.stubOffset())
1281                          .get();
1282 
1283   if (IsInsideNursery(*prototypeObject)) return false;
1284 
1285   if (!reader.matchOp(CacheOp::GuardFunctionPrototype, rhsId)) return false;
1286 
1287   reader.skip();  // Skip over the protoID;
1288 
1289   *slot = stub->stubInfo()->getStubRawWord(stub, reader.stubOffset());
1290 
1291   return true;
1292 }
1293