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