1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
7 #include "jit/BaselineInspector.h"
9 #include "mozilla/Array.h"
10 #include "mozilla/DebugOnly.h"
12 #include "jit/BaselineIC.h"
13 #include "jit/CacheIRCompiler.h"
15 #include "vm/EnvironmentObject-inl.h"
16 #include "vm/JSScript-inl.h"
17 #include "vm/ObjectGroup-inl.h"
18 #include "vm/ReceiverGuard-inl.h"
21 using namespace js::jit
;
23 using mozilla::DebugOnly
;
25 bool SetElemICInspector::sawOOBDenseWrite() const {
30 // Check for a write hole bit on the SetElem_Fallback stub.
31 ICStub
* stub
= icEntry_
->fallbackStub();
32 if (stub
->isSetElem_Fallback()) {
33 return stub
->toSetElem_Fallback()->hasDenseAdd();
39 bool SetElemICInspector::sawOOBTypedArrayWrite() const {
44 ICStub
* stub
= icEntry_
->fallbackStub();
45 if (stub
->isSetElem_Fallback()) {
46 return stub
->toSetElem_Fallback()->hasTypedArrayOOB();
52 template <typename S
, typename T
>
53 static bool VectorAppendNoDuplicate(S
& list
, T value
) {
54 for (size_t i
= 0; i
< list
.length(); i
++) {
55 if (list
[i
] == value
) {
59 return list
.append(value
);
62 static bool AddReceiver(const ReceiverGuard
& receiver
,
63 BaselineInspector::ReceiverVector
& receivers
) {
64 return VectorAppendNoDuplicate(receivers
, receiver
);
67 static bool GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored
* stub
,
68 ReceiverGuard
* receiver
) {
73 // LoadFixedSlotResult 0 or LoadDynamicSlotResult 0
75 *receiver
= ReceiverGuard();
76 CacheIRReader
reader(stub
->stubInfo());
78 ObjOperandId objId
= ObjOperandId(0);
79 if (!reader
.matchOp(CacheOp::GuardToObject
, objId
)) {
83 if (reader
.matchOp(CacheOp::GuardShape
, objId
)) {
85 stub
->stubInfo()->getStubField
<Shape
*>(stub
, reader
.stubOffset()));
86 return reader
.matchOpEither(CacheOp::LoadFixedSlotResult
,
87 CacheOp::LoadDynamicSlotResult
);
93 static bool GetCacheIRReceiverForNativeSetSlot(ICCacheIR_Updated
* stub
,
94 ReceiverGuard
* receiver
) {
100 // StoreFixedSlot 0 or StoreDynamicSlot 0
101 *receiver
= ReceiverGuard();
102 CacheIRReader
reader(stub
->stubInfo());
104 ObjOperandId objId
= ObjOperandId(0);
105 if (!reader
.matchOp(CacheOp::GuardToObject
, objId
)) {
109 if (!reader
.matchOp(CacheOp::GuardGroup
, objId
)) {
113 stub
->stubInfo()->getStubField
<ObjectGroup
*>(stub
, reader
.stubOffset());
115 if (!reader
.matchOp(CacheOp::GuardShape
, objId
)) {
119 stub
->stubInfo()->getStubField
<Shape
*>(stub
, reader
.stubOffset());
121 if (!reader
.matchOpEither(CacheOp::StoreFixedSlot
,
122 CacheOp::StoreDynamicSlot
)) {
126 *receiver
= ReceiverGuard(group
, shape
);
130 JitScript
* BaselineInspector::jitScript() const { return script
->jitScript(); }
132 ICEntry
& BaselineInspector::icEntryFromPC(jsbytecode
* pc
) {
133 ICEntry
* entry
= maybeICEntryFromPC(pc
);
138 ICEntry
* BaselineInspector::maybeICEntryFromPC(jsbytecode
* pc
) {
139 MOZ_ASSERT(isValidPC(pc
));
140 ICEntry
* ent
= jitScript()->maybeICEntryFromPCOffset(script
->pcToOffset(pc
),
146 MOZ_ASSERT(!ent
->isForPrologue());
147 prevLookedUpEntry
= ent
;
151 bool BaselineInspector::maybeInfoForPropertyOp(jsbytecode
* pc
,
152 ReceiverVector
& receivers
) {
153 // Return a list of the receivers seen by the baseline IC for the current
154 // op. Empty lists indicate no receivers are known, or there was an
155 // uncacheable access.
156 MOZ_ASSERT(receivers
.empty());
158 MOZ_ASSERT(isValidPC(pc
));
159 const ICEntry
& entry
= icEntryFromPC(pc
);
161 ICStub
* stub
= entry
.firstStub();
162 while (stub
->next()) {
163 ReceiverGuard receiver
;
164 if (stub
->isCacheIR_Monitored()) {
165 if (!GetCacheIRReceiverForNativeReadSlot(stub
->toCacheIR_Monitored(),
170 } else if (stub
->isCacheIR_Updated()) {
171 if (!GetCacheIRReceiverForNativeSetSlot(stub
->toCacheIR_Updated(),
181 if (!AddReceiver(receiver
, receivers
)) {
188 if (stub
->toFallbackStub()->state().hasFailures()) {
192 // Don't inline if there are more than 5 receivers.
193 if (receivers
.length() > 5) {
200 ICStub
* BaselineInspector::monomorphicStub(jsbytecode
* pc
) {
201 // IonBuilder::analyzeNewLoopTypes may call this (via expectedResultType
202 // below) on code that's unreachable, according to BytecodeAnalysis. Use
203 // maybeICEntryFromPC to handle this.
204 const ICEntry
* entry
= maybeICEntryFromPC(pc
);
209 ICStub
* stub
= entry
->firstStub();
210 ICStub
* next
= stub
->next();
212 if (!next
|| !next
->isFallback()) {
219 bool BaselineInspector::dimorphicStub(jsbytecode
* pc
, ICStub
** pfirst
,
221 const ICEntry
& entry
= icEntryFromPC(pc
);
223 ICStub
* stub
= entry
.firstStub();
224 ICStub
* next
= stub
->next();
225 ICStub
* after
= next
? next
->next() : nullptr;
227 if (!after
|| !after
->isFallback()) {
236 // Process the type guards in the stub in order to reveal the
237 // underlying operation.
238 static void SkipBinaryGuards(CacheIRReader
& reader
, bool* sawStringOperand
) {
241 if (reader
.matchOp(CacheOp::GuardNonDoubleType
) ||
242 reader
.matchOp(CacheOp::TruncateDoubleToUInt32
) ||
243 reader
.matchOp(CacheOp::GuardBooleanToInt32
)) {
244 reader
.skip(); // Skip over operandId
245 reader
.skip(); // Skip over result/type.
248 if (reader
.matchOp(CacheOp::GuardStringToNumber
) ||
249 reader
.matchOp(CacheOp::GuardStringToInt32
)) {
250 if (sawStringOperand
) {
251 *sawStringOperand
= true;
253 reader
.skip(); // Skip over operandId
254 reader
.skip(); // Skip over result.
259 if (reader
.matchOp(CacheOp::GuardToInt32
) ||
260 reader
.matchOp(CacheOp::GuardIsNumber
) ||
261 reader
.matchOp(CacheOp::GuardToString
) ||
262 reader
.matchOp(CacheOp::GuardToObject
) ||
263 reader
.matchOp(CacheOp::GuardToBigInt
) ||
264 reader
.matchOp(CacheOp::GuardToBoolean
)) {
265 reader
.skip(); // Skip over operandId
272 static MIRType
ParseCacheIRStub(ICStub
* stub
,
273 bool* sawStringOperand
= nullptr) {
274 ICCacheIR_Regular
* cacheirStub
= stub
->toCacheIR_Regular();
275 CacheIRReader
reader(cacheirStub
->stubInfo());
276 SkipBinaryGuards(reader
, sawStringOperand
);
277 switch (reader
.readOp()) {
278 case CacheOp::LoadUndefinedResult
:
279 return MIRType::Undefined
;
280 case CacheOp::LoadBooleanResult
:
281 return MIRType::Boolean
;
282 case CacheOp::LoadStringResult
:
283 case CacheOp::CallStringConcatResult
:
284 case CacheOp::CallStringObjectConcatResult
:
285 case CacheOp::CallInt32ToString
:
286 case CacheOp::BooleanToString
:
287 case CacheOp::CallNumberToString
:
288 return MIRType::String
;
289 case CacheOp::LoadDoubleResult
:
290 case CacheOp::DoubleAddResult
:
291 case CacheOp::DoubleSubResult
:
292 case CacheOp::DoubleMulResult
:
293 case CacheOp::DoubleDivResult
:
294 case CacheOp::DoubleModResult
:
295 case CacheOp::DoublePowResult
:
296 case CacheOp::DoubleNegationResult
:
297 case CacheOp::DoubleIncResult
:
298 case CacheOp::DoubleDecResult
:
299 return MIRType::Double
;
300 case CacheOp::LoadInt32Result
:
301 case CacheOp::Int32AddResult
:
302 case CacheOp::Int32SubResult
:
303 case CacheOp::Int32MulResult
:
304 case CacheOp::Int32DivResult
:
305 case CacheOp::Int32ModResult
:
306 case CacheOp::Int32PowResult
:
307 case CacheOp::Int32BitOrResult
:
308 case CacheOp::Int32BitXorResult
:
309 case CacheOp::Int32BitAndResult
:
310 case CacheOp::Int32LeftShiftResult
:
311 case CacheOp::Int32RightShiftResult
:
312 case CacheOp::Int32NotResult
:
313 case CacheOp::Int32NegationResult
:
314 case CacheOp::Int32IncResult
:
315 case CacheOp::Int32DecResult
:
316 return MIRType::Int32
;
317 // Int32URightShiftResult may return a double under some
319 case CacheOp::Int32URightShiftResult
:
320 reader
.skip(); // Skip over lhs
321 reader
.skip(); // Skip over rhs
322 return reader
.readByte() == 0 ? MIRType::Int32
: MIRType::Double
;
323 case CacheOp::BigIntAddResult
:
324 case CacheOp::BigIntSubResult
:
325 case CacheOp::BigIntMulResult
:
326 case CacheOp::BigIntDivResult
:
327 case CacheOp::BigIntModResult
:
328 case CacheOp::BigIntPowResult
:
329 case CacheOp::BigIntBitOrResult
:
330 case CacheOp::BigIntBitXorResult
:
331 case CacheOp::BigIntBitAndResult
:
332 case CacheOp::BigIntLeftShiftResult
:
333 case CacheOp::BigIntRightShiftResult
:
334 case CacheOp::BigIntNotResult
:
335 case CacheOp::BigIntNegationResult
:
336 case CacheOp::BigIntIncResult
:
337 case CacheOp::BigIntDecResult
:
338 return MIRType::BigInt
;
339 case CacheOp::LoadValueResult
:
340 return MIRType::Value
;
342 MOZ_CRASH("Unknown op");
343 return MIRType::None
;
347 MIRType
BaselineInspector::expectedResultType(jsbytecode
* pc
) {
348 // Look at the IC entries for this op to guess what type it will produce,
349 // returning MIRType::None otherwise. Note that IonBuilder may call this
350 // for bytecode ops that are unreachable and don't have a Baseline IC, see
351 // comment in monomorphicStub.
353 ICStub
* stub
= monomorphicStub(pc
);
355 return MIRType::None
;
358 switch (stub
->kind()) {
359 case ICStub::CacheIR_Regular
:
360 return ParseCacheIRStub(stub
);
362 return MIRType::None
;
366 // Return the MIRtype corresponding to the guard the reader is pointing
367 // to, and ensure that afterwards the reader is pointing to the next op
368 // (consume operands).
370 // An expected parameter is provided to allow GuardType to check we read
371 // the guard from the expected operand in debug builds.
372 static bool GuardType(CacheIRReader
& reader
,
373 mozilla::Array
<MIRType
, 2>& guardType
) {
374 CacheOp op
= reader
.readOp();
375 uint8_t guardOperand
= reader
.readByte();
377 // We only have two entries for guard types.
378 if (guardOperand
> 1) {
382 // Already assigned this guard a type, fail.
383 if (guardType
[guardOperand
] != MIRType::None
) {
389 case CacheOp::GuardToString
:
390 guardType
[guardOperand
] = MIRType::String
;
392 case CacheOp::GuardToSymbol
:
393 guardType
[guardOperand
] = MIRType::Symbol
;
395 case CacheOp::GuardToBigInt
:
396 guardType
[guardOperand
] = MIRType::BigInt
;
398 case CacheOp::GuardToBoolean
:
399 guardType
[guardOperand
] = MIRType::Boolean
;
401 case CacheOp::GuardToInt32
:
402 guardType
[guardOperand
] = MIRType::Int32
;
404 case CacheOp::GuardIsNumber
:
405 guardType
[guardOperand
] = MIRType::Double
;
407 case CacheOp::GuardIsUndefined
:
408 guardType
[guardOperand
] = MIRType::Undefined
;
411 case CacheOp::GuardBooleanToInt32
:
412 guardType
[guardOperand
] = MIRType::Boolean
;
423 // This code works for all Compare ICs where the pattern is
429 // in other cases (like StrictlyDifferentTypes) it will just
430 // return CompareUnknown
431 static MCompare::CompareType
ParseCacheIRStubForCompareType(
432 ICCacheIR_Regular
* stub
) {
433 CacheIRReader
reader(stub
->stubInfo());
435 // Two element array to allow parsing the guards
436 // in whichever order they appear.
437 mozilla::Array
<MIRType
, 2> guards
= {MIRType::None
, MIRType::None
};
439 // Parse out two guards
440 if (!GuardType(reader
, guards
)) {
441 return MCompare::Compare_Unknown
;
443 if (!GuardType(reader
, guards
)) {
444 return MCompare::Compare_Unknown
;
447 // The lhs and rhs ids are asserted in
448 // CompareIRGenerator::tryAttachStub.
449 MIRType lhs_guard
= guards
[0];
450 MIRType rhs_guard
= guards
[1];
452 if (lhs_guard
== rhs_guard
) {
453 if (lhs_guard
== MIRType::Int32
) {
454 return MCompare::Compare_Int32
;
456 if (lhs_guard
== MIRType::Double
) {
457 return MCompare::Compare_Double
;
459 return MCompare::Compare_Unknown
;
462 if ((lhs_guard
== MIRType::Int32
&& rhs_guard
== MIRType::Boolean
) ||
463 (lhs_guard
== MIRType::Boolean
&& rhs_guard
== MIRType::Int32
)) {
465 if (rhs_guard
== MIRType::Boolean
) {
466 return MCompare::Compare_Int32MaybeCoerceRHS
;
469 return MCompare::Compare_Int32MaybeCoerceLHS
;
472 if ((lhs_guard
== MIRType::Double
&& rhs_guard
== MIRType::Undefined
) ||
473 (lhs_guard
== MIRType::Undefined
&& rhs_guard
== MIRType::Double
)) {
475 if (rhs_guard
== MIRType::Undefined
) {
476 return MCompare::Compare_DoubleMaybeCoerceRHS
;
479 return MCompare::Compare_DoubleMaybeCoerceLHS
;
482 return MCompare::Compare_Unknown
;
485 static bool CoercingCompare(MCompare::CompareType type
) {
486 // Prefer the coercing types if they exist, otherwise just use first's type.
487 if (type
== MCompare::Compare_DoubleMaybeCoerceLHS
||
488 type
== MCompare::Compare_DoubleMaybeCoerceRHS
||
489 type
== MCompare::Compare_Int32MaybeCoerceLHS
||
490 type
== MCompare::Compare_Int32MaybeCoerceRHS
) {
496 static MCompare::CompareType
CompatibleType(MCompare::CompareType first
,
497 MCompare::CompareType second
) {
498 // Caller should have dealt with this case.
499 MOZ_ASSERT(first
!= second
);
501 // Prefer the coercing types if they exist, otherwise, return Double,
502 // which is the general cover.
503 if (CoercingCompare(first
)) {
507 if (CoercingCompare(second
)) {
511 // At this point we expect a Double/Int32 pair, so we return Double.
512 MOZ_ASSERT(first
== MCompare::Compare_Int32
||
513 first
== MCompare::Compare_Double
);
514 MOZ_ASSERT(second
== MCompare::Compare_Int32
||
515 second
== MCompare::Compare_Double
);
516 MOZ_ASSERT(first
!= second
);
518 return MCompare::Compare_Double
;
521 MCompare::CompareType
BaselineInspector::expectedCompareType(jsbytecode
* pc
) {
522 ICStub
* first
= monomorphicStub(pc
);
523 ICStub
* second
= nullptr;
524 if (!first
&& !dimorphicStub(pc
, &first
, &second
)) {
525 return MCompare::Compare_Unknown
;
528 if (ICStub
* fallback
= second
? second
->next() : first
->next()) {
529 MOZ_ASSERT(fallback
->isFallback());
530 if (fallback
->toFallbackStub()->state().hasFailures()) {
531 return MCompare::Compare_Unknown
;
535 MCompare::CompareType first_type
=
536 ParseCacheIRStubForCompareType(first
->toCacheIR_Regular());
541 MCompare::CompareType second_type
=
542 ParseCacheIRStubForCompareType(second
->toCacheIR_Regular());
544 if (first_type
== MCompare::Compare_Unknown
||
545 second_type
== MCompare::Compare_Unknown
) {
546 return MCompare::Compare_Unknown
;
549 if (first_type
== second_type
) {
553 return CompatibleType(first_type
, second_type
);
556 static bool TryToSpecializeBinaryArithOp(ICStub
** stubs
, uint32_t nstubs
,
558 DebugOnly
<bool> sawInt32
= false;
559 bool sawDouble
= false;
560 bool sawOther
= false;
561 bool sawStringOperand
= false;
563 for (uint32_t i
= 0; i
< nstubs
; i
++) {
564 switch (stubs
[i
]->kind()) {
565 case ICStub::CacheIR_Regular
:
566 switch (ParseCacheIRStub(stubs
[i
], &sawStringOperand
)) {
567 case MIRType::Double
:
588 // Ion doesn't support string operands for binary arithmetic operations, so
589 // return false when we did see one. We don't test for strings in Ion itself,
590 // because Ion generally doesn't have sufficient type information when it
591 // falls back to the baseline inspector.
592 if (sawStringOperand
) {
597 *result
= MIRType::Double
;
601 MOZ_ASSERT(sawInt32
);
602 *result
= MIRType::Int32
;
606 MIRType
BaselineInspector::expectedBinaryArithSpecialization(jsbytecode
* pc
) {
610 const ICEntry
& entry
= icEntryFromPC(pc
);
611 ICFallbackStub
* stub
= entry
.fallbackStub();
612 if (stub
->state().hasFailures()) {
613 return MIRType::None
;
616 stubs
[0] = monomorphicStub(pc
);
618 if (TryToSpecializeBinaryArithOp(stubs
, 1, &result
)) {
623 if (dimorphicStub(pc
, &stubs
[0], &stubs
[1])) {
624 if (TryToSpecializeBinaryArithOp(stubs
, 2, &result
)) {
629 return MIRType::None
;
632 bool BaselineInspector::hasSeenNonIntegerIndex(jsbytecode
* pc
) {
633 const ICEntry
& entry
= icEntryFromPC(pc
);
634 ICStub
* stub
= entry
.fallbackStub();
636 MOZ_ASSERT(stub
->isGetElem_Fallback());
638 return stub
->toGetElem_Fallback()->sawNonIntegerIndex();
641 bool BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode
* pc
) {
642 const ICEntry
& entry
= icEntryFromPC(pc
);
643 ICStub
* stub
= entry
.fallbackStub();
645 if (stub
->isGetElem_Fallback()) {
646 return stub
->toGetElem_Fallback()->hasNegativeIndex();
651 bool BaselineInspector::hasSeenAccessedGetter(jsbytecode
* pc
) {
652 const ICEntry
& entry
= icEntryFromPC(pc
);
653 ICStub
* stub
= entry
.fallbackStub();
655 if (stub
->isGetProp_Fallback()) {
656 return stub
->toGetProp_Fallback()->hasAccessedGetter();
661 bool BaselineInspector::hasSeenDoubleResult(jsbytecode
* pc
) {
662 const ICEntry
& entry
= icEntryFromPC(pc
);
663 ICStub
* stub
= entry
.fallbackStub();
665 MOZ_ASSERT(stub
->isUnaryArith_Fallback() || stub
->isBinaryArith_Fallback());
667 if (stub
->isUnaryArith_Fallback()) {
668 return stub
->toUnaryArith_Fallback()->sawDoubleResult();
670 return stub
->toBinaryArith_Fallback()->sawDoubleResult();
673 static bool MaybeArgumentReader(ICStub
* stub
, CacheOp targetOp
,
674 mozilla::Maybe
<CacheIRReader
>& argReader
) {
675 MOZ_ASSERT(ICStub::IsCacheIRKind(stub
->kind()));
677 CacheIRReader
stubReader(stub
->cacheIRStubInfo());
678 while (stubReader
.more()) {
679 CacheOp op
= stubReader
.readOp();
680 uint32_t argLength
= CacheIROpArgLengths
[size_t(op
)];
682 if (op
== targetOp
) {
683 MOZ_ASSERT(argReader
.isNothing(),
684 "Multiple instances of an op are not currently supported");
685 const uint8_t* argStart
= stubReader
.currentPosition();
686 argReader
.emplace(argStart
, argStart
+ argLength
);
689 // Advance to next opcode.
690 stubReader
.skip(argLength
);
692 return argReader
.isSome();
695 template <typename Filter
>
696 JSObject
* MaybeTemplateObject(ICStub
* stub
, MetaTwoByteKind kind
,
698 const CacheIRStubInfo
* stubInfo
= stub
->cacheIRStubInfo();
699 mozilla::Maybe
<CacheIRReader
> argReader
;
700 if (!MaybeArgumentReader(stub
, CacheOp::MetaTwoByte
, argReader
) ||
701 argReader
->metaKind
<MetaTwoByteKind
>() != kind
||
702 !filter(*argReader
, stubInfo
)) {
705 return stubInfo
->getStubField
<JSObject
*>(stub
, argReader
->stubOffset());
708 JSObject
* BaselineInspector::getTemplateObject(jsbytecode
* pc
) {
709 const ICEntry
& entry
= icEntryFromPC(pc
);
710 for (ICStub
* stub
= entry
.firstStub(); stub
; stub
= stub
->next()) {
711 switch (stub
->kind()) {
712 case ICStub::NewArray_Fallback
:
713 return stub
->toNewArray_Fallback()->templateObject();
714 case ICStub::NewObject_Fallback
:
715 return stub
->toNewObject_Fallback()->templateObject();
716 case ICStub::Rest_Fallback
:
717 return stub
->toRest_Fallback()->templateObject();
718 case ICStub::CacheIR_Regular
:
719 case ICStub::CacheIR_Monitored
:
720 case ICStub::CacheIR_Updated
: {
721 auto filter
= [](CacheIRReader
& reader
, const CacheIRStubInfo
* info
) {
722 mozilla::Unused
<< reader
.stubOffset(); // Skip callee
725 JSObject
* result
= MaybeTemplateObject(
726 stub
, MetaTwoByteKind::ScriptedTemplateObject
, filter
);
739 ObjectGroup
* BaselineInspector::getTemplateObjectGroup(jsbytecode
* pc
) {
740 const ICEntry
& entry
= icEntryFromPC(pc
);
741 for (ICStub
* stub
= entry
.firstStub(); stub
; stub
= stub
->next()) {
742 switch (stub
->kind()) {
743 case ICStub::NewArray_Fallback
:
744 return stub
->toNewArray_Fallback()->templateGroup();
753 JSFunction
* BaselineInspector::getSingleCallee(jsbytecode
* pc
) {
754 MOZ_ASSERT(IsConstructPC(pc
));
756 const ICEntry
& entry
= icEntryFromPC(pc
);
757 ICStub
* stub
= entry
.firstStub();
759 if (entry
.fallbackStub()->state().hasFailures()) {
763 if (stub
->next() != entry
.fallbackStub()) {
767 if (ICStub::IsCacheIRKind(stub
->kind())) {
768 const CacheIRStubInfo
* stubInfo
= stub
->cacheIRStubInfo();
769 mozilla::Maybe
<CacheIRReader
> argReader
;
770 if (!MaybeArgumentReader(stub
, CacheOp::MetaTwoByte
, argReader
) ||
771 argReader
->metaKind
<MetaTwoByteKind
>() !=
772 MetaTwoByteKind::ScriptedTemplateObject
) {
775 // The callee is the first stub field in a ScriptedTemplateObject meta op.
776 return stubInfo
->getStubField
<JSFunction
*>(stub
, argReader
->stubOffset());
781 JSObject
* BaselineInspector::getTemplateObjectForNative(jsbytecode
* pc
,
783 const ICEntry
& entry
= icEntryFromPC(pc
);
784 for (ICStub
* stub
= entry
.firstStub(); stub
; stub
= stub
->next()) {
785 if (ICStub::IsCacheIRKind(stub
->kind())) {
786 auto filter
= [stub
, native
](CacheIRReader
& reader
,
787 const CacheIRStubInfo
* info
) {
789 info
->getStubField
<JSFunction
*>(stub
, reader
.stubOffset());
790 return callee
->native() == native
;
792 JSObject
* result
= MaybeTemplateObject(
793 stub
, MetaTwoByteKind::NativeTemplateObject
, filter
);
803 LexicalEnvironmentObject
* BaselineInspector::templateNamedLambdaObject() {
804 JSObject
* res
= jitScript()->templateEnvironment();
805 if (script
->bodyScope()->hasEnvironment()) {
806 res
= res
->enclosingEnvironment();
810 return &res
->as
<LexicalEnvironmentObject
>();
813 CallObject
* BaselineInspector::templateCallObject() {
814 JSObject
* res
= jitScript()->templateEnvironment();
817 return &res
->as
<CallObject
>();
820 static bool MatchCacheIRReceiverGuard(CacheIRReader
& reader
, ICStub
* stub
,
821 const CacheIRStubInfo
* stubInfo
,
823 ReceiverGuard
* receiver
) {
824 // This matches the CacheIR emitted in TestMatchingReceiver.
830 *receiver
= ReceiverGuard();
832 if (reader
.matchOp(CacheOp::GuardShape
, objId
)) {
835 stubInfo
->getStubField
<Shape
*>(stub
, reader
.stubOffset()));
841 static bool AddCacheIRGlobalGetter(ICCacheIR_Monitored
* stub
, bool innerized
,
842 JSObject
** holder_
, Shape
** holderShape_
,
843 JSFunction
** commonGetter
,
844 Shape
** globalShape_
, bool* isOwnProperty
,
845 BaselineInspector::ReceiverVector
& receivers
,
847 // We are matching on the IR generated by tryAttachGlobalNameGetter:
850 // globalId = LoadEnclosingEnvironment objId
851 // GuardShape globalId
852 // <holderId = LoadObject <holder>>
853 // <GuardShape holderId>
854 // CallNativeGetterResult globalId
860 CacheIRReader
reader(stub
->stubInfo());
862 ObjOperandId objId
= ObjOperandId(0);
863 if (!reader
.matchOp(CacheOp::GuardShape
, objId
)) {
866 Shape
* globalLexicalShape
=
867 stub
->stubInfo()->getStubField
<Shape
*>(stub
, reader
.stubOffset());
869 if (!reader
.matchOp(CacheOp::LoadEnclosingEnvironment
, objId
)) {
872 ObjOperandId globalId
= reader
.objOperandId();
874 if (!reader
.matchOp(CacheOp::GuardShape
, globalId
)) {
878 stub
->stubInfo()->getStubField
<Shape
*>(stub
, reader
.stubOffset());
879 MOZ_ASSERT(globalShape
->getObjectClass()->flags
& JSCLASS_IS_GLOBAL
);
881 JSObject
* holder
= &script
->global();
882 Shape
* holderShape
= globalShape
;
883 if (reader
.matchOp(CacheOp::LoadObject
)) {
884 ObjOperandId holderId
= reader
.objOperandId();
885 holder
= stub
->stubInfo()
886 ->getStubField
<JSObject
*>(stub
, reader
.stubOffset())
889 if (!reader
.matchOp(CacheOp::GuardShape
, holderId
)) {
893 stub
->stubInfo()->getStubField
<Shape
*>(stub
, reader
.stubOffset());
896 // This guard will always fail, try the next stub.
897 if (holder
->as
<NativeObject
>().lastProperty() != holderShape
) {
901 if (!reader
.matchOp(CacheOp::CallNativeGetterResult
, globalId
)) {
904 size_t offset
= reader
.stubOffset();
905 JSFunction
* getter
= &stub
->stubInfo()
906 ->getStubField
<JSObject
*>(stub
, offset
)
909 ReceiverGuard receiver
;
910 receiver
.setShape(globalLexicalShape
);
911 if (!AddReceiver(receiver
, receivers
)) {
915 if (!*commonGetter
) {
917 *holderShape_
= holderShape
;
918 *commonGetter
= getter
;
919 *globalShape_
= globalShape
;
921 // This is always false, because the getters never live on the
923 *isOwnProperty
= false;
924 } else if (*isOwnProperty
|| holderShape
!= *holderShape_
||
925 globalShape
!= *globalShape_
) {
928 MOZ_ASSERT(*commonGetter
== getter
);
934 static bool GuardSpecificAtomOrSymbol(CacheIRReader
& reader
, ICStub
* stub
,
935 const CacheIRStubInfo
* stubInfo
,
936 ValOperandId keyId
, jsid id
) {
937 // Try to match an id guard emitted by IRGenerator::emitIdGuard.
938 if (JSID_IS_ATOM(id
)) {
939 if (!reader
.matchOp(CacheOp::GuardToString
, keyId
)) {
942 if (!reader
.matchOp(CacheOp::GuardSpecificAtom
, keyId
)) {
946 stubInfo
->getStubField
<JSString
*>(stub
, reader
.stubOffset()).get();
947 if (AtomToId(&str
->asAtom()) != id
) {
951 MOZ_ASSERT(JSID_IS_SYMBOL(id
));
952 if (!reader
.matchOp(CacheOp::GuardToSymbol
, keyId
)) {
955 if (!reader
.matchOp(CacheOp::GuardSpecificSymbol
, keyId
)) {
959 stubInfo
->getStubField
<Symbol
*>(stub
, reader
.stubOffset()).get();
960 if (SYMBOL_TO_JSID(sym
) != id
) {
968 static bool AddCacheIRGetPropFunction(
969 ICCacheIR_Monitored
* stub
, jsid id
, bool innerized
, JSObject
** holder
,
970 Shape
** holderShape
, JSFunction
** commonGetter
, Shape
** globalShape
,
971 bool* isOwnProperty
, BaselineInspector::ReceiverVector
& receivers
,
973 // We match either an own getter:
975 // GuardToObject objId
977 // [..WindowProxy innerization..]
978 // <GuardReceiver objId>
979 // (Call(Scripted|Native)Getter|TypedArrayLength)Result objId
981 // Or a getter on the prototype:
983 // GuardToObject objId
985 // [..WindowProxy innerization..]
986 // <GuardReceiver objId>
987 // LoadObject holderId
988 // GuardShape holderId
989 // (Call(Scripted|Native)Getter|TypedArrayLength)Result objId
991 // If |innerized| is true, we replaced a WindowProxy with the Window
992 // object and we're only interested in Baseline getter stubs that performed
993 // the same optimization. This means we expect the following ops for the
994 // [..WindowProxy innerization..] above:
996 // GuardClass objId WindowProxy
997 // objId = LoadWrapperTarget objId
998 // GuardSpecificObject objId, <global>
1000 // If we test for a specific jsid, [..Id Guard..] is implemented through:
1001 // GuardIs(String|Symbol) keyId
1002 // GuardSpecific(Atom|Symbol) keyId, <atom|symbol>
1004 CacheIRReader
reader(stub
->stubInfo());
1006 ObjOperandId objId
= ObjOperandId(0);
1007 if (!reader
.matchOp(CacheOp::GuardToObject
, objId
)) {
1008 return AddCacheIRGlobalGetter(stub
, innerized
, holder
, holderShape
,
1009 commonGetter
, globalShape
, isOwnProperty
,
1013 if (!JSID_IS_EMPTY(id
)) {
1014 ValOperandId keyId
= ValOperandId(1);
1015 if (!GuardSpecificAtomOrSymbol(reader
, stub
, stub
->stubInfo(), keyId
, id
)) {
1021 if (!reader
.matchOp(CacheOp::GuardClass
, objId
) ||
1022 reader
.guardClassKind() != GuardClassKind::WindowProxy
) {
1026 if (!reader
.matchOp(CacheOp::LoadWrapperTarget
, objId
)) {
1029 objId
= reader
.objOperandId();
1031 if (!reader
.matchOp(CacheOp::GuardSpecificObject
, objId
)) {
1034 DebugOnly
<JSObject
*> obj
=
1036 ->getStubField
<JSObject
*>(stub
, reader
.stubOffset())
1038 MOZ_ASSERT(obj
->is
<GlobalObject
>());
1041 ReceiverGuard receiver
;
1042 if (!MatchCacheIRReceiverGuard(reader
, stub
, stub
->stubInfo(), objId
,
1047 if (reader
.matchOp(CacheOp::CallScriptedGetterResult
, objId
) ||
1048 reader
.matchOp(CacheOp::CallNativeGetterResult
, objId
) ||
1049 reader
.matchOp(CacheOp::LoadTypedArrayLengthResult
, objId
)) {
1050 // This is an own property getter, the first case.
1051 MOZ_ASSERT(receiver
.getShape());
1052 MOZ_ASSERT(!receiver
.getGroup());
1054 size_t offset
= reader
.stubOffset();
1055 JSFunction
* getter
= &stub
->stubInfo()
1056 ->getStubField
<JSObject
*>(stub
, offset
)
1059 if (*commonGetter
&& (!*isOwnProperty
|| *globalShape
||
1060 *holderShape
!= receiver
.getShape())) {
1064 MOZ_ASSERT_IF(*commonGetter
, *commonGetter
== getter
);
1066 *holderShape
= receiver
.getShape();
1067 *commonGetter
= getter
;
1068 *isOwnProperty
= true;
1072 if (!reader
.matchOp(CacheOp::LoadObject
)) {
1075 ObjOperandId holderId
= reader
.objOperandId();
1077 stub
->stubInfo()->getStubField
<JSObject
*>(stub
, reader
.stubOffset());
1079 if (!reader
.matchOp(CacheOp::GuardShape
, holderId
)) {
1083 stub
->stubInfo()->getStubField
<Shape
*>(stub
, reader
.stubOffset());
1085 if (!reader
.matchOp(CacheOp::CallScriptedGetterResult
, objId
) &&
1086 !reader
.matchOp(CacheOp::CallNativeGetterResult
, objId
) &&
1087 !reader
.matchOp(CacheOp::LoadTypedArrayLengthResult
, objId
)) {
1091 // A getter on the prototype.
1092 size_t offset
= reader
.stubOffset();
1093 JSFunction
* getter
= &stub
->stubInfo()
1094 ->getStubField
<JSObject
*>(stub
, offset
)
1097 Shape
* thisGlobalShape
= nullptr;
1098 if (getter
->isNative() && receiver
.getShape() &&
1099 (receiver
.getShape()->getObjectClass()->flags
& JSCLASS_IS_GLOBAL
)) {
1100 thisGlobalShape
= receiver
.getShape();
1103 if (*commonGetter
&& (*isOwnProperty
|| *globalShape
!= thisGlobalShape
||
1104 *holderShape
!= objShape
)) {
1108 MOZ_ASSERT_IF(*commonGetter
, *commonGetter
== getter
);
1110 if (obj
->as
<NativeObject
>().lastProperty() != objShape
) {
1111 // Skip this stub as the shape is no longer correct.
1115 if (!AddReceiver(receiver
, receivers
)) {
1120 *holderShape
= objShape
;
1121 *commonGetter
= getter
;
1122 *isOwnProperty
= false;
1126 bool BaselineInspector::commonGetPropFunction(
1127 jsbytecode
* pc
, jsid id
, bool innerized
, JSObject
** holder
,
1128 Shape
** holderShape
, JSFunction
** commonGetter
, Shape
** globalShape
,
1129 bool* isOwnProperty
, ReceiverVector
& receivers
) {
1130 MOZ_ASSERT(IsGetPropPC(pc
) || IsGetElemPC(pc
) || JSOp(*pc
) == JSOp::GetGName
);
1131 MOZ_ASSERT(receivers
.empty());
1133 // Only GetElem operations need to guard against a specific property id.
1134 if (!IsGetElemPC(pc
)) {
1138 *globalShape
= nullptr;
1139 *commonGetter
= nullptr;
1140 const ICEntry
& entry
= icEntryFromPC(pc
);
1142 for (ICStub
* stub
= entry
.firstStub(); stub
; stub
= stub
->next()) {
1143 if (stub
->isCacheIR_Monitored()) {
1144 if (!AddCacheIRGetPropFunction(
1145 stub
->toCacheIR_Monitored(), id
, innerized
, holder
, holderShape
,
1146 commonGetter
, globalShape
, isOwnProperty
, receivers
, script
)) {
1149 } else if (stub
->isFallback()) {
1150 // If we have an unoptimizable access, don't try to optimize.
1151 if (stub
->toFallbackStub()->state().hasFailures()) {
1159 if (!*commonGetter
) {
1163 MOZ_ASSERT(*isOwnProperty
== !*holder
);
1164 MOZ_ASSERT(*isOwnProperty
== receivers
.empty());
1168 static JSFunction
* GetMegamorphicGetterSetterFunction(
1169 ICStub
* stub
, const CacheIRStubInfo
* stubInfo
, jsid id
, bool isGetter
) {
1172 // GuardToObject objId
1174 // GuardHasGetterSetter objId propShape
1176 // propShape has the getter/setter we're interested in.
1178 // If we test for a specific jsid, [..Id Guard..] is implemented through:
1179 // GuardIs(String|Symbol) keyId
1180 // GuardSpecific(Atom|Symbol) keyId, <atom|symbol>
1182 CacheIRReader
reader(stubInfo
);
1184 ObjOperandId objId
= ObjOperandId(0);
1185 if (!reader
.matchOp(CacheOp::GuardToObject
, objId
)) {
1189 if (!JSID_IS_EMPTY(id
)) {
1190 ValOperandId keyId
= ValOperandId(1);
1191 if (!GuardSpecificAtomOrSymbol(reader
, stub
, stubInfo
, keyId
, id
)) {
1196 if (!reader
.matchOp(CacheOp::GuardHasGetterSetter
, objId
)) {
1199 Shape
* propShape
= stubInfo
->getStubField
<Shape
*>(stub
, reader
.stubOffset());
1202 isGetter
? propShape
->getterObject() : propShape
->setterObject();
1203 return &obj
->as
<JSFunction
>();
1206 bool BaselineInspector::megamorphicGetterSetterFunction(
1207 jsbytecode
* pc
, jsid id
, bool isGetter
, JSFunction
** getterOrSetter
) {
1208 MOZ_ASSERT(IsGetPropPC(pc
) || IsGetElemPC(pc
) || IsSetPropPC(pc
) ||
1209 JSOp(*pc
) == JSOp::GetGName
|| JSOp(*pc
) == JSOp::InitGLexical
||
1210 JSOp(*pc
) == JSOp::InitProp
|| JSOp(*pc
) == JSOp::InitLockedProp
||
1211 JSOp(*pc
) == JSOp::InitHiddenProp
);
1213 // Only GetElem operations need to guard against a specific property id.
1214 if (!IsGetElemPC(pc
)) {
1218 *getterOrSetter
= nullptr;
1219 const ICEntry
& entry
= icEntryFromPC(pc
);
1221 for (ICStub
* stub
= entry
.firstStub(); stub
; stub
= stub
->next()) {
1222 if (stub
->isCacheIR_Monitored()) {
1223 MOZ_ASSERT(isGetter
);
1224 JSFunction
* getter
= GetMegamorphicGetterSetterFunction(
1225 stub
, stub
->toCacheIR_Monitored()->stubInfo(), id
, isGetter
);
1226 if (!getter
|| (*getterOrSetter
&& *getterOrSetter
!= getter
)) {
1229 *getterOrSetter
= getter
;
1232 if (stub
->isCacheIR_Updated()) {
1233 MOZ_ASSERT(!isGetter
);
1234 JSFunction
* setter
= GetMegamorphicGetterSetterFunction(
1235 stub
, stub
->toCacheIR_Updated()->stubInfo(), id
, isGetter
);
1236 if (!setter
|| (*getterOrSetter
&& *getterOrSetter
!= setter
)) {
1239 *getterOrSetter
= setter
;
1242 if (stub
->isFallback()) {
1243 if (stub
->toFallbackStub()->state().hasFailures()) {
1246 if (stub
->toFallbackStub()->state().mode() !=
1247 ICState::Mode::Megamorphic
) {
1256 if (!*getterOrSetter
) {
1263 static bool AddCacheIRSetPropFunction(
1264 ICCacheIR_Updated
* stub
, JSObject
** holder
, Shape
** holderShape
,
1265 JSFunction
** commonSetter
, bool* isOwnProperty
,
1266 BaselineInspector::ReceiverVector
& receivers
) {
1267 // We match either an own setter:
1269 // GuardToObject objId
1270 // <GuardReceiver objId>
1271 // Call(Scripted|Native)Setter objId
1273 // Or a setter on the prototype:
1275 // GuardToObject objId
1276 // <GuardReceiver objId>
1277 // LoadObject holderId
1278 // GuardShape holderId
1279 // Call(Scripted|Native)Setter objId
1281 CacheIRReader
reader(stub
->stubInfo());
1283 ObjOperandId objId
= ObjOperandId(0);
1284 if (!reader
.matchOp(CacheOp::GuardToObject
, objId
)) {
1288 ReceiverGuard receiver
;
1289 if (!MatchCacheIRReceiverGuard(reader
, stub
, stub
->stubInfo(), objId
,
1294 if (reader
.matchOp(CacheOp::CallScriptedSetter
, objId
) ||
1295 reader
.matchOp(CacheOp::CallNativeSetter
, objId
)) {
1296 // This is an own property setter, the first case.
1297 MOZ_ASSERT(receiver
.getShape());
1298 MOZ_ASSERT(!receiver
.getGroup());
1300 size_t offset
= reader
.stubOffset();
1301 JSFunction
* setter
= &stub
->stubInfo()
1302 ->getStubField
<JSObject
*>(stub
, offset
)
1305 if (*commonSetter
&&
1306 (!*isOwnProperty
|| *holderShape
!= receiver
.getShape())) {
1310 MOZ_ASSERT_IF(*commonSetter
, *commonSetter
== setter
);
1312 *holderShape
= receiver
.getShape();
1313 *commonSetter
= setter
;
1314 *isOwnProperty
= true;
1318 if (!reader
.matchOp(CacheOp::LoadObject
)) {
1321 ObjOperandId holderId
= reader
.objOperandId();
1323 stub
->stubInfo()->getStubField
<JSObject
*>(stub
, reader
.stubOffset());
1325 if (!reader
.matchOp(CacheOp::GuardShape
, holderId
)) {
1329 stub
->stubInfo()->getStubField
<Shape
*>(stub
, reader
.stubOffset());
1331 if (!reader
.matchOp(CacheOp::CallScriptedSetter
, objId
) &&
1332 !reader
.matchOp(CacheOp::CallNativeSetter
, objId
)) {
1336 // A setter on the prototype.
1337 size_t offset
= reader
.stubOffset();
1338 JSFunction
* setter
= &stub
->stubInfo()
1339 ->getStubField
<JSObject
*>(stub
, offset
)
1342 if (*commonSetter
&& (*isOwnProperty
|| *holderShape
!= objShape
)) {
1346 MOZ_ASSERT_IF(*commonSetter
, *commonSetter
== setter
);
1348 if (obj
->as
<NativeObject
>().lastProperty() != objShape
) {
1349 // Skip this stub as the shape is no longer correct.
1353 if (!AddReceiver(receiver
, receivers
)) {
1358 *holderShape
= objShape
;
1359 *commonSetter
= setter
;
1360 *isOwnProperty
= false;
1364 bool BaselineInspector::commonSetPropFunction(jsbytecode
* pc
, JSObject
** holder
,
1365 Shape
** holderShape
,
1366 JSFunction
** commonSetter
,
1367 bool* isOwnProperty
,
1368 ReceiverVector
& receivers
) {
1369 MOZ_ASSERT(IsSetPropPC(pc
) || JSOp(*pc
) == JSOp::InitGLexical
||
1370 JSOp(*pc
) == JSOp::InitProp
|| JSOp(*pc
) == JSOp::InitLockedProp
||
1371 JSOp(*pc
) == JSOp::InitHiddenProp
);
1372 MOZ_ASSERT(receivers
.empty());
1374 *commonSetter
= nullptr;
1375 const ICEntry
& entry
= icEntryFromPC(pc
);
1377 for (ICStub
* stub
= entry
.firstStub(); stub
; stub
= stub
->next()) {
1378 if (stub
->isCacheIR_Updated()) {
1379 if (!AddCacheIRSetPropFunction(stub
->toCacheIR_Updated(), holder
,
1380 holderShape
, commonSetter
, isOwnProperty
,
1384 } else if (!stub
->isFallback() ||
1385 stub
->toFallbackStub()->state().hasFailures()) {
1386 // We have an unoptimizable access, so don't try to optimize.
1391 if (!*commonSetter
) {
1395 MOZ_ASSERT(*isOwnProperty
== !*holder
);
1399 static bool GetCacheIRReceiverForProtoReadSlot(ICCacheIR_Monitored
* stub
,
1400 ReceiverGuard
* receiver
,
1401 JSObject
** holderResult
) {
1406 // 1: LoadObject holder
1408 // LoadFixedSlotResult 1 or LoadDynamicSlotResult 1
1410 *receiver
= ReceiverGuard();
1411 CacheIRReader
reader(stub
->stubInfo());
1413 ObjOperandId objId
= ObjOperandId(0);
1414 if (!reader
.matchOp(CacheOp::GuardToObject
, objId
)) {
1418 if (!MatchCacheIRReceiverGuard(reader
, stub
, stub
->stubInfo(), objId
,
1423 if (!reader
.matchOp(CacheOp::LoadObject
)) {
1426 ObjOperandId holderId
= reader
.objOperandId();
1427 JSObject
* holder
= stub
->stubInfo()
1428 ->getStubField
<JSObject
*>(stub
, reader
.stubOffset())
1431 if (!reader
.matchOp(CacheOp::GuardShape
, holderId
)) {
1434 Shape
* holderShape
=
1435 stub
->stubInfo()->getStubField
<Shape
*>(stub
, reader
.stubOffset());
1437 if (!reader
.matchOpEither(CacheOp::LoadFixedSlotResult
,
1438 CacheOp::LoadDynamicSlotResult
)) {
1441 if (reader
.objOperandId() != holderId
) {
1445 if (holder
->shape() != holderShape
) {
1448 if (*holderResult
&& *holderResult
!= holder
) {
1452 *holderResult
= holder
;
1456 bool BaselineInspector::maybeInfoForProtoReadSlot(jsbytecode
* pc
,
1457 ReceiverVector
& receivers
,
1458 JSObject
** holder
) {
1459 // This is like maybeInfoForPropertyOp, but for when the property exists on
1462 MOZ_ASSERT(receivers
.empty());
1463 MOZ_ASSERT(!*holder
);
1465 MOZ_ASSERT(isValidPC(pc
));
1466 const ICEntry
& entry
= icEntryFromPC(pc
);
1468 ICStub
* stub
= entry
.firstStub();
1469 while (stub
->next()) {
1470 ReceiverGuard receiver
;
1471 if (stub
->isCacheIR_Monitored()) {
1472 if (!GetCacheIRReceiverForProtoReadSlot(stub
->toCacheIR_Monitored(),
1473 &receiver
, holder
)) {
1482 if (!AddReceiver(receiver
, receivers
)) {
1486 stub
= stub
->next();
1489 if (stub
->toFallbackStub()->state().hasFailures()) {
1493 // Don't inline if there are more than 5 receivers.
1494 if (receivers
.length() > 5) {
1498 MOZ_ASSERT_IF(!receivers
.empty(), *holder
);
1502 static MIRType
GetCacheIRExpectedInputType(ICCacheIR_Monitored
* stub
) {
1503 CacheIRReader
reader(stub
->stubInfo());
1505 if (reader
.matchOp(CacheOp::GuardToObject
, ValOperandId(0))) {
1506 return MIRType::Object
;
1508 if (reader
.matchOp(CacheOp::GuardToString
, ValOperandId(0))) {
1509 return MIRType::String
;
1511 if (reader
.matchOp(CacheOp::GuardIsNumber
, ValOperandId(0))) {
1512 return MIRType::Double
;
1514 if (reader
.matchOp(CacheOp::GuardNonDoubleType
, ValOperandId(0))) {
1515 ValueType type
= reader
.valueType();
1516 return MIRTypeFromValueType(JSValueType(type
));
1518 if (reader
.matchOp(CacheOp::GuardMagicValue
)) {
1519 // This can happen if we attached a lazy-args Baseline IC stub but then
1520 // called JSScript::argumentsOptimizationFailed.
1521 return MIRType::Value
;
1524 MOZ_ASSERT_UNREACHABLE("Unexpected instruction");
1525 return MIRType::Value
;
1528 MIRType
BaselineInspector::expectedPropertyAccessInputType(jsbytecode
* pc
) {
1529 const ICEntry
& entry
= icEntryFromPC(pc
);
1530 MIRType type
= MIRType::None
;
1532 for (ICStub
* stub
= entry
.firstStub(); stub
; stub
= stub
->next()) {
1533 MIRType stubType
= MIRType::None
;
1534 if (stub
->isCacheIR_Monitored()) {
1535 stubType
= GetCacheIRExpectedInputType(stub
->toCacheIR_Monitored());
1536 if (stubType
== MIRType::Value
) {
1537 return MIRType::Value
;
1539 } else if (stub
->isGetElem_Fallback() || stub
->isGetProp_Fallback()) {
1540 // If we have an unoptimizable access, don't try to optimize.
1541 if (stub
->toFallbackStub()->state().hasFailures()) {
1542 return MIRType::Value
;
1545 MOZ_CRASH("Unexpected stub");
1548 if (type
!= MIRType::None
) {
1549 if (type
!= stubType
) {
1550 return MIRType::Value
;
1557 return (type
== MIRType::None
) ? MIRType::Value
: type
;
1560 bool BaselineInspector::instanceOfData(jsbytecode
* pc
, Shape
** shape
,
1562 JSObject
** prototypeObject
) {
1563 MOZ_ASSERT(JSOp(*pc
) == JSOp::Instanceof
);
1565 const ICEntry
& entry
= icEntryFromPC(pc
);
1566 ICStub
* firstStub
= entry
.firstStub();
1568 // Ensure singleton instanceof stub
1569 if (!firstStub
->next() || !firstStub
->isCacheIR_Regular() ||
1570 !firstStub
->next()->isInstanceOf_Fallback() ||
1571 firstStub
->next()->toInstanceOf_Fallback()->state().hasFailures()) {
1575 ICCacheIR_Regular
* stub
= entry
.firstStub()->toCacheIR_Regular();
1576 CacheIRReader
reader(stub
->stubInfo());
1578 ObjOperandId rhsId
= ObjOperandId(1);
1579 ObjOperandId resId
= ObjOperandId(2);
1581 if (!reader
.matchOp(CacheOp::GuardToObject
, rhsId
)) {
1585 if (!reader
.matchOp(CacheOp::GuardShape
, rhsId
)) {
1589 *shape
= stub
->stubInfo()->getStubField
<Shape
*>(stub
, reader
.stubOffset());
1591 if (!reader
.matchOp(CacheOp::LoadObject
, resId
)) {
1595 *prototypeObject
= stub
->stubInfo()
1596 ->getStubField
<JSObject
*>(stub
, reader
.stubOffset())
1599 if (IsInsideNursery(*prototypeObject
)) {
1603 if (!reader
.matchOp(CacheOp::GuardDynamicSlotIsSpecificObject
, rhsId
)) {
1607 reader
.skip(); // Skip over the protoID;
1609 *slot
= stub
->stubInfo()->getStubRawWord(stub
, reader
.stubOffset());