Bug 1639153 - Part 6.2: Establish dependency from tls for x86 callWithABI div/mod...
[gecko.git] / js / src / jit / BaselineInspector.cpp
bloba7fa30db29a4dba4e76f70adef5b94d8a5b705a2
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"
20 using namespace js;
21 using namespace js::jit;
23 using mozilla::DebugOnly;
25 bool SetElemICInspector::sawOOBDenseWrite() const {
26 if (!icEntry_) {
27 return false;
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();
36 return false;
39 bool SetElemICInspector::sawOOBTypedArrayWrite() const {
40 if (!icEntry_) {
41 return false;
44 ICStub* stub = icEntry_->fallbackStub();
45 if (stub->isSetElem_Fallback()) {
46 return stub->toSetElem_Fallback()->hasTypedArrayOOB();
49 return false;
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) {
56 return true;
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) {
69 // We match:
71 // GuardToObject 0
72 // GuardShape 0
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)) {
80 return false;
83 if (reader.matchOp(CacheOp::GuardShape, objId)) {
84 receiver->setShape(
85 stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset()));
86 return reader.matchOpEither(CacheOp::LoadFixedSlotResult,
87 CacheOp::LoadDynamicSlotResult);
90 return false;
93 static bool GetCacheIRReceiverForNativeSetSlot(ICCacheIR_Updated* stub,
94 ReceiverGuard* receiver) {
95 // We match:
97 // GuardToObject 0
98 // GuardGroup 0
99 // GuardShape 0
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)) {
106 return false;
109 if (!reader.matchOp(CacheOp::GuardGroup, objId)) {
110 return false;
112 ObjectGroup* group =
113 stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
115 if (!reader.matchOp(CacheOp::GuardShape, objId)) {
116 return false;
118 Shape* shape =
119 stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
121 if (!reader.matchOpEither(CacheOp::StoreFixedSlot,
122 CacheOp::StoreDynamicSlot)) {
123 return false;
126 *receiver = ReceiverGuard(group, shape);
127 return true;
130 JitScript* BaselineInspector::jitScript() const { return script->jitScript(); }
132 ICEntry& BaselineInspector::icEntryFromPC(jsbytecode* pc) {
133 ICEntry* entry = maybeICEntryFromPC(pc);
134 MOZ_ASSERT(entry);
135 return *entry;
138 ICEntry* BaselineInspector::maybeICEntryFromPC(jsbytecode* pc) {
139 MOZ_ASSERT(isValidPC(pc));
140 ICEntry* ent = jitScript()->maybeICEntryFromPCOffset(script->pcToOffset(pc),
141 prevLookedUpEntry);
142 if (!ent) {
143 return nullptr;
146 MOZ_ASSERT(!ent->isForPrologue());
147 prevLookedUpEntry = ent;
148 return 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(),
166 &receiver)) {
167 receivers.clear();
168 return true;
170 } else if (stub->isCacheIR_Updated()) {
171 if (!GetCacheIRReceiverForNativeSetSlot(stub->toCacheIR_Updated(),
172 &receiver)) {
173 receivers.clear();
174 return true;
176 } else {
177 receivers.clear();
178 return true;
181 if (!AddReceiver(receiver, receivers)) {
182 return false;
185 stub = stub->next();
188 if (stub->toFallbackStub()->state().hasFailures()) {
189 receivers.clear();
192 // Don't inline if there are more than 5 receivers.
193 if (receivers.length() > 5) {
194 receivers.clear();
197 return true;
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);
205 if (!entry) {
206 return nullptr;
209 ICStub* stub = entry->firstStub();
210 ICStub* next = stub->next();
212 if (!next || !next->isFallback()) {
213 return nullptr;
216 return stub;
219 bool BaselineInspector::dimorphicStub(jsbytecode* pc, ICStub** pfirst,
220 ICStub** psecond) {
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()) {
228 return false;
231 *pfirst = stub;
232 *psecond = next;
233 return true;
236 // Process the type guards in the stub in order to reveal the
237 // underlying operation.
238 static void SkipBinaryGuards(CacheIRReader& reader, bool* sawStringOperand) {
239 while (true) {
240 // Two skip opcodes
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.
246 continue;
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.
255 continue;
258 // One skip
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
266 continue;
268 return;
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
318 // circumstances.
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;
341 default:
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);
354 if (!stub) {
355 return MIRType::None;
358 switch (stub->kind()) {
359 case ICStub::CacheIR_Regular:
360 return ParseCacheIRStub(stub);
361 default:
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) {
379 return false;
382 // Already assigned this guard a type, fail.
383 if (guardType[guardOperand] != MIRType::None) {
384 return false;
387 switch (op) {
388 // 0 Skip cases
389 case CacheOp::GuardToString:
390 guardType[guardOperand] = MIRType::String;
391 break;
392 case CacheOp::GuardToSymbol:
393 guardType[guardOperand] = MIRType::Symbol;
394 break;
395 case CacheOp::GuardToBigInt:
396 guardType[guardOperand] = MIRType::BigInt;
397 break;
398 case CacheOp::GuardToBoolean:
399 guardType[guardOperand] = MIRType::Boolean;
400 break;
401 case CacheOp::GuardToInt32:
402 guardType[guardOperand] = MIRType::Int32;
403 break;
404 case CacheOp::GuardIsNumber:
405 guardType[guardOperand] = MIRType::Double;
406 break;
407 case CacheOp::GuardIsUndefined:
408 guardType[guardOperand] = MIRType::Undefined;
409 break;
410 // 1 skip
411 case CacheOp::GuardBooleanToInt32:
412 guardType[guardOperand] = MIRType::Boolean;
413 // Skip over result
414 reader.skip();
415 break;
416 // Unknown op --
417 default:
418 return false;
420 return true;
423 // This code works for all Compare ICs where the pattern is
425 // <Guard LHS/RHS>
426 // <Guard RHS/LHS>
427 // <CompareResult>
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)) {
464 // RHS is converting
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)) {
474 // RHS is converting
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) {
491 return true;
493 return false;
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)) {
504 return first;
507 if (CoercingCompare(second)) {
508 return 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());
537 if (!second) {
538 return first_type;
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) {
550 return first_type;
553 return CompatibleType(first_type, second_type);
556 static bool TryToSpecializeBinaryArithOp(ICStub** stubs, uint32_t nstubs,
557 MIRType* result) {
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:
568 sawDouble = true;
569 break;
570 case MIRType::Int32:
571 sawInt32 = true;
572 break;
573 default:
574 sawOther = true;
575 break;
577 break;
578 default:
579 sawOther = true;
580 break;
584 if (sawOther) {
585 return false;
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) {
593 return false;
596 if (sawDouble) {
597 *result = MIRType::Double;
598 return true;
601 MOZ_ASSERT(sawInt32);
602 *result = MIRType::Int32;
603 return true;
606 MIRType BaselineInspector::expectedBinaryArithSpecialization(jsbytecode* pc) {
607 MIRType result;
608 ICStub* stubs[2];
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);
617 if (stubs[0]) {
618 if (TryToSpecializeBinaryArithOp(stubs, 1, &result)) {
619 return result;
623 if (dimorphicStub(pc, &stubs[0], &stubs[1])) {
624 if (TryToSpecializeBinaryArithOp(stubs, 2, &result)) {
625 return 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();
648 return false;
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();
658 return false;
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,
697 Filter filter) {
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)) {
703 return nullptr;
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
723 return true;
725 JSObject* result = MaybeTemplateObject(
726 stub, MetaTwoByteKind::ScriptedTemplateObject, filter);
727 if (result) {
728 return result;
730 } break;
731 default:
732 break;
736 return nullptr;
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();
745 default:
746 break;
750 return nullptr;
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()) {
760 return nullptr;
763 if (stub->next() != entry.fallbackStub()) {
764 return nullptr;
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) {
773 return nullptr;
775 // The callee is the first stub field in a ScriptedTemplateObject meta op.
776 return stubInfo->getStubField<JSFunction*>(stub, argReader->stubOffset());
778 return nullptr;
781 JSObject* BaselineInspector::getTemplateObjectForNative(jsbytecode* pc,
782 Native native) {
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) {
788 JSFunction* callee =
789 info->getStubField<JSFunction*>(stub, reader.stubOffset());
790 return callee->native() == native;
792 JSObject* result = MaybeTemplateObject(
793 stub, MetaTwoByteKind::NativeTemplateObject, filter);
794 if (result) {
795 return result;
800 return nullptr;
803 LexicalEnvironmentObject* BaselineInspector::templateNamedLambdaObject() {
804 JSObject* res = jitScript()->templateEnvironment();
805 if (script->bodyScope()->hasEnvironment()) {
806 res = res->enclosingEnvironment();
808 MOZ_ASSERT(res);
810 return &res->as<LexicalEnvironmentObject>();
813 CallObject* BaselineInspector::templateCallObject() {
814 JSObject* res = jitScript()->templateEnvironment();
815 MOZ_ASSERT(res);
817 return &res->as<CallObject>();
820 static bool MatchCacheIRReceiverGuard(CacheIRReader& reader, ICStub* stub,
821 const CacheIRStubInfo* stubInfo,
822 ObjOperandId objId,
823 ReceiverGuard* receiver) {
824 // This matches the CacheIR emitted in TestMatchingReceiver.
826 // Either:
828 // GuardShape objId
830 *receiver = ReceiverGuard();
832 if (reader.matchOp(CacheOp::GuardShape, objId)) {
833 // The first case.
834 receiver->setShape(
835 stubInfo->getStubField<Shape*>(stub, reader.stubOffset()));
836 return true;
838 return false;
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,
846 JSScript* script) {
847 // We are matching on the IR generated by tryAttachGlobalNameGetter:
849 // GuardShape objId
850 // globalId = LoadEnclosingEnvironment objId
851 // GuardShape globalId
852 // <holderId = LoadObject <holder>>
853 // <GuardShape holderId>
854 // CallNativeGetterResult globalId
856 if (innerized) {
857 return false;
860 CacheIRReader reader(stub->stubInfo());
862 ObjOperandId objId = ObjOperandId(0);
863 if (!reader.matchOp(CacheOp::GuardShape, objId)) {
864 return false;
866 Shape* globalLexicalShape =
867 stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
869 if (!reader.matchOp(CacheOp::LoadEnclosingEnvironment, objId)) {
870 return false;
872 ObjOperandId globalId = reader.objOperandId();
874 if (!reader.matchOp(CacheOp::GuardShape, globalId)) {
875 return false;
877 Shape* globalShape =
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())
887 .get();
889 if (!reader.matchOp(CacheOp::GuardShape, holderId)) {
890 return false;
892 holderShape =
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) {
898 return true;
901 if (!reader.matchOp(CacheOp::CallNativeGetterResult, globalId)) {
902 return false;
904 size_t offset = reader.stubOffset();
905 JSFunction* getter = &stub->stubInfo()
906 ->getStubField<JSObject*>(stub, offset)
907 ->as<JSFunction>();
909 ReceiverGuard receiver;
910 receiver.setShape(globalLexicalShape);
911 if (!AddReceiver(receiver, receivers)) {
912 return false;
915 if (!*commonGetter) {
916 *holder_ = holder;
917 *holderShape_ = holderShape;
918 *commonGetter = getter;
919 *globalShape_ = globalShape;
921 // This is always false, because the getters never live on the
922 // globalLexical.
923 *isOwnProperty = false;
924 } else if (*isOwnProperty || holderShape != *holderShape_ ||
925 globalShape != *globalShape_) {
926 return false;
927 } else {
928 MOZ_ASSERT(*commonGetter == getter);
931 return true;
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)) {
940 return false;
942 if (!reader.matchOp(CacheOp::GuardSpecificAtom, keyId)) {
943 return false;
945 JSString* str =
946 stubInfo->getStubField<JSString*>(stub, reader.stubOffset()).get();
947 if (AtomToId(&str->asAtom()) != id) {
948 return false;
950 } else {
951 MOZ_ASSERT(JSID_IS_SYMBOL(id));
952 if (!reader.matchOp(CacheOp::GuardToSymbol, keyId)) {
953 return false;
955 if (!reader.matchOp(CacheOp::GuardSpecificSymbol, keyId)) {
956 return false;
958 Symbol* sym =
959 stubInfo->getStubField<Symbol*>(stub, reader.stubOffset()).get();
960 if (SYMBOL_TO_JSID(sym) != id) {
961 return false;
965 return true;
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,
972 JSScript* script) {
973 // We match either an own getter:
975 // GuardToObject objId
976 // [..Id Guard..]
977 // [..WindowProxy innerization..]
978 // <GuardReceiver objId>
979 // (Call(Scripted|Native)Getter|TypedArrayLength)Result objId
981 // Or a getter on the prototype:
983 // GuardToObject objId
984 // [..Id Guard..]
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,
1010 receivers, script);
1013 if (!JSID_IS_EMPTY(id)) {
1014 ValOperandId keyId = ValOperandId(1);
1015 if (!GuardSpecificAtomOrSymbol(reader, stub, stub->stubInfo(), keyId, id)) {
1016 return false;
1020 if (innerized) {
1021 if (!reader.matchOp(CacheOp::GuardClass, objId) ||
1022 reader.guardClassKind() != GuardClassKind::WindowProxy) {
1023 return false;
1026 if (!reader.matchOp(CacheOp::LoadWrapperTarget, objId)) {
1027 return false;
1029 objId = reader.objOperandId();
1031 if (!reader.matchOp(CacheOp::GuardSpecificObject, objId)) {
1032 return false;
1034 DebugOnly<JSObject*> obj =
1035 stub->stubInfo()
1036 ->getStubField<JSObject*>(stub, reader.stubOffset())
1037 .get();
1038 MOZ_ASSERT(obj->is<GlobalObject>());
1041 ReceiverGuard receiver;
1042 if (!MatchCacheIRReceiverGuard(reader, stub, stub->stubInfo(), objId,
1043 &receiver)) {
1044 return false;
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)
1057 ->as<JSFunction>();
1059 if (*commonGetter && (!*isOwnProperty || *globalShape ||
1060 *holderShape != receiver.getShape())) {
1061 return false;
1064 MOZ_ASSERT_IF(*commonGetter, *commonGetter == getter);
1065 *holder = nullptr;
1066 *holderShape = receiver.getShape();
1067 *commonGetter = getter;
1068 *isOwnProperty = true;
1069 return true;
1072 if (!reader.matchOp(CacheOp::LoadObject)) {
1073 return false;
1075 ObjOperandId holderId = reader.objOperandId();
1076 JSObject* obj =
1077 stub->stubInfo()->getStubField<JSObject*>(stub, reader.stubOffset());
1079 if (!reader.matchOp(CacheOp::GuardShape, holderId)) {
1080 return false;
1082 Shape* objShape =
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)) {
1088 return false;
1091 // A getter on the prototype.
1092 size_t offset = reader.stubOffset();
1093 JSFunction* getter = &stub->stubInfo()
1094 ->getStubField<JSObject*>(stub, offset)
1095 ->as<JSFunction>();
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)) {
1105 return false;
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.
1112 return true;
1115 if (!AddReceiver(receiver, receivers)) {
1116 return false;
1119 *holder = obj;
1120 *holderShape = objShape;
1121 *commonGetter = getter;
1122 *isOwnProperty = false;
1123 return true;
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)) {
1135 id = JSID_EMPTY;
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)) {
1147 return false;
1149 } else if (stub->isFallback()) {
1150 // If we have an unoptimizable access, don't try to optimize.
1151 if (stub->toFallbackStub()->state().hasFailures()) {
1152 return false;
1154 } else {
1155 return false;
1159 if (!*commonGetter) {
1160 return false;
1163 MOZ_ASSERT(*isOwnProperty == !*holder);
1164 MOZ_ASSERT(*isOwnProperty == receivers.empty());
1165 return true;
1168 static JSFunction* GetMegamorphicGetterSetterFunction(
1169 ICStub* stub, const CacheIRStubInfo* stubInfo, jsid id, bool isGetter) {
1170 // We match:
1172 // GuardToObject objId
1173 // [..Id Guard..]
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)) {
1186 return nullptr;
1189 if (!JSID_IS_EMPTY(id)) {
1190 ValOperandId keyId = ValOperandId(1);
1191 if (!GuardSpecificAtomOrSymbol(reader, stub, stubInfo, keyId, id)) {
1192 return nullptr;
1196 if (!reader.matchOp(CacheOp::GuardHasGetterSetter, objId)) {
1197 return nullptr;
1199 Shape* propShape = stubInfo->getStubField<Shape*>(stub, reader.stubOffset());
1201 JSObject* obj =
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)) {
1215 id = JSID_EMPTY;
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)) {
1227 return false;
1229 *getterOrSetter = getter;
1230 continue;
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)) {
1237 return false;
1239 *getterOrSetter = setter;
1240 continue;
1242 if (stub->isFallback()) {
1243 if (stub->toFallbackStub()->state().hasFailures()) {
1244 return false;
1246 if (stub->toFallbackStub()->state().mode() !=
1247 ICState::Mode::Megamorphic) {
1248 return false;
1250 continue;
1253 return false;
1256 if (!*getterOrSetter) {
1257 return false;
1260 return true;
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)) {
1285 return false;
1288 ReceiverGuard receiver;
1289 if (!MatchCacheIRReceiverGuard(reader, stub, stub->stubInfo(), objId,
1290 &receiver)) {
1291 return false;
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)
1303 ->as<JSFunction>();
1305 if (*commonSetter &&
1306 (!*isOwnProperty || *holderShape != receiver.getShape())) {
1307 return false;
1310 MOZ_ASSERT_IF(*commonSetter, *commonSetter == setter);
1311 *holder = nullptr;
1312 *holderShape = receiver.getShape();
1313 *commonSetter = setter;
1314 *isOwnProperty = true;
1315 return true;
1318 if (!reader.matchOp(CacheOp::LoadObject)) {
1319 return false;
1321 ObjOperandId holderId = reader.objOperandId();
1322 JSObject* obj =
1323 stub->stubInfo()->getStubField<JSObject*>(stub, reader.stubOffset());
1325 if (!reader.matchOp(CacheOp::GuardShape, holderId)) {
1326 return false;
1328 Shape* objShape =
1329 stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
1331 if (!reader.matchOp(CacheOp::CallScriptedSetter, objId) &&
1332 !reader.matchOp(CacheOp::CallNativeSetter, objId)) {
1333 return false;
1336 // A setter on the prototype.
1337 size_t offset = reader.stubOffset();
1338 JSFunction* setter = &stub->stubInfo()
1339 ->getStubField<JSObject*>(stub, offset)
1340 ->as<JSFunction>();
1342 if (*commonSetter && (*isOwnProperty || *holderShape != objShape)) {
1343 return false;
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.
1350 return true;
1353 if (!AddReceiver(receiver, receivers)) {
1354 return false;
1357 *holder = obj;
1358 *holderShape = objShape;
1359 *commonSetter = setter;
1360 *isOwnProperty = false;
1361 return true;
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,
1381 receivers)) {
1382 return false;
1384 } else if (!stub->isFallback() ||
1385 stub->toFallbackStub()->state().hasFailures()) {
1386 // We have an unoptimizable access, so don't try to optimize.
1387 return false;
1391 if (!*commonSetter) {
1392 return false;
1395 MOZ_ASSERT(*isOwnProperty == !*holder);
1396 return true;
1399 static bool GetCacheIRReceiverForProtoReadSlot(ICCacheIR_Monitored* stub,
1400 ReceiverGuard* receiver,
1401 JSObject** holderResult) {
1402 // We match:
1404 // GuardToObject 0
1405 // <ReceiverGuard>
1406 // 1: LoadObject holder
1407 // GuardShape 1
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)) {
1415 return false;
1418 if (!MatchCacheIRReceiverGuard(reader, stub, stub->stubInfo(), objId,
1419 receiver)) {
1420 return false;
1423 if (!reader.matchOp(CacheOp::LoadObject)) {
1424 return false;
1426 ObjOperandId holderId = reader.objOperandId();
1427 JSObject* holder = stub->stubInfo()
1428 ->getStubField<JSObject*>(stub, reader.stubOffset())
1429 .get();
1431 if (!reader.matchOp(CacheOp::GuardShape, holderId)) {
1432 return false;
1434 Shape* holderShape =
1435 stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
1437 if (!reader.matchOpEither(CacheOp::LoadFixedSlotResult,
1438 CacheOp::LoadDynamicSlotResult)) {
1439 return false;
1441 if (reader.objOperandId() != holderId) {
1442 return false;
1445 if (holder->shape() != holderShape) {
1446 return false;
1448 if (*holderResult && *holderResult != holder) {
1449 return false;
1452 *holderResult = holder;
1453 return true;
1456 bool BaselineInspector::maybeInfoForProtoReadSlot(jsbytecode* pc,
1457 ReceiverVector& receivers,
1458 JSObject** holder) {
1459 // This is like maybeInfoForPropertyOp, but for when the property exists on
1460 // the prototype.
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)) {
1474 receivers.clear();
1475 return true;
1477 } else {
1478 receivers.clear();
1479 return true;
1482 if (!AddReceiver(receiver, receivers)) {
1483 return false;
1486 stub = stub->next();
1489 if (stub->toFallbackStub()->state().hasFailures()) {
1490 receivers.clear();
1493 // Don't inline if there are more than 5 receivers.
1494 if (receivers.length() > 5) {
1495 receivers.clear();
1498 MOZ_ASSERT_IF(!receivers.empty(), *holder);
1499 return true;
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;
1544 } else {
1545 MOZ_CRASH("Unexpected stub");
1548 if (type != MIRType::None) {
1549 if (type != stubType) {
1550 return MIRType::Value;
1552 } else {
1553 type = stubType;
1557 return (type == MIRType::None) ? MIRType::Value : type;
1560 bool BaselineInspector::instanceOfData(jsbytecode* pc, Shape** shape,
1561 uint32_t* slot,
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()) {
1572 return false;
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)) {
1582 return false;
1585 if (!reader.matchOp(CacheOp::GuardShape, rhsId)) {
1586 return false;
1589 *shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
1591 if (!reader.matchOp(CacheOp::LoadObject, resId)) {
1592 return false;
1595 *prototypeObject = stub->stubInfo()
1596 ->getStubField<JSObject*>(stub, reader.stubOffset())
1597 .get();
1599 if (IsInsideNursery(*prototypeObject)) {
1600 return false;
1603 if (!reader.matchOp(CacheOp::GuardDynamicSlotIsSpecificObject, rhsId)) {
1604 return false;
1607 reader.skip(); // Skip over the protoID;
1609 *slot = stub->stubInfo()->getStubRawWord(stub, reader.stubOffset());
1611 return true;