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