bool SetElemICInspector::sawTypedArrayWrite() const { if (!icEntry_) return false; // Check for a SetElem_TypedArray stub. for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) { if (stub->isSetElem_TypedArray()) return true; } return false; }
bool BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode *pc) { if (!hasBaselineScript()) return false; const ICEntry &entry = icEntryFromPC(pc); ICStub *stub = entry.fallbackStub(); if (stub->isGetElem_Fallback()) return stub->toGetElem_Fallback()->hasNegativeIndex(); return false; }
bool SetElemICInspector::sawDenseWrite() const { if (!icEntry_) return false; // Check for a SetElem_DenseAdd or SetElem_Dense stub. for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) { if (stub->isSetElem_DenseAdd() || stub->isSetElem_Dense()) return true; } return false; }
bool BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc) { if (!hasBaselineScript()) return false; const ICEntry &entry = icEntryFromPC(pc); ICStub *stub = entry.fallbackStub(); if (stub->isGetProp_Fallback()) return stub->toGetProp_Fallback()->hasAccessedGetter(); return false; }
bool BaselineInspector::hasSeenNonStringIterMore(jsbytecode* pc) { MOZ_ASSERT(JSOp(*pc) == JSOP_MOREITER); if (!hasBaselineScript()) return false; const ICEntry& entry = icEntryFromPC(pc); ICStub* stub = entry.fallbackStub(); return stub->toIteratorMore_Fallback()->hasNonStringResult(); }
JSObject * BaselineInspector::getTemplateObjectForNative(jsbytecode *pc, Native native) { if (!hasBaselineScript()) return nullptr; const ICEntry &entry = icEntryFromPC(pc); for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isCall_Native() && stub->toCall_Native()->callee()->native() == native) return stub->toCall_Native()->templateObject(); } return nullptr; }
JSObject * BaselineInspector::commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter) { const ICEntry &entry = icEntryFromPC(pc); for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) { ICSetPropCallSetter *nstub = static_cast<ICSetPropCallSetter *>(stub); *lastProperty = nstub->holderShape(); *commonSetter = nstub->setter(); return nstub->holder(); } } return nullptr; }
JSObject* BaselineInspector::getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp) { if (!hasBaselineScript()) return nullptr; const ICEntry& entry = icEntryFromPC(pc); for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isCall_ClassHook() && stub->toCall_ClassHook()->clasp() == clasp) return stub->toCall_ClassHook()->templateObject(); } return nullptr; }
bool SetElemICInspector::sawOOBTypedArrayWrite() const { if (!icEntry_) return false; // Check for SetElem_TypedArray stubs with expectOutOfBounds set. for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) { if (!stub->isSetElem_TypedArray()) continue; if (stub->toSetElem_TypedArray()->expectOutOfBounds()) return true; } return false; }
ICStub * BaselineInspector::monomorphicStub(jsbytecode *pc) { if (!hasBaselineScript()) return NULL; const ICEntry &entry = icEntryFromPC(pc); ICStub *stub = entry.firstStub(); ICStub *next = stub->next(); if (!next || !next->isFallback()) return NULL; return stub; }
bool BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty, ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups) { if (!hasBaselineScript()) return false; MOZ_ASSERT(receivers.empty()); MOZ_ASSERT(convertUnboxedGroups.empty()); *holder = nullptr; const ICEntry& entry = icEntryFromPC(pc); for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isGetProp_CallScripted() || stub->isGetProp_CallNative()) { ICGetPropCallGetter* nstub = static_cast<ICGetPropCallGetter*>(stub); bool isOwn = nstub->isOwnGetter(); if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups)) return false; if (!*holder) { *holder = nstub->holder(); *holderShape = nstub->holderShape(); *commonGetter = nstub->getter(); *globalShape = GlobalShapeForGetPropFunction(nstub); *isOwnProperty = isOwn; } else if (nstub->holderShape() != *holderShape || GlobalShapeForGetPropFunction(nstub) != *globalShape || isOwn != *isOwnProperty) { return false; } else { MOZ_ASSERT(*commonGetter == nstub->getter()); } } else if (stub->isGetProp_Fallback()) { // If we have an unoptimizable access, don't try to optimize. if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) return false; } else if (stub->isGetName_Fallback()) { if (stub->toGetName_Fallback()->hadUnoptimizableAccess()) return false; } else { return false; } } if (!*holder) return false; MOZ_ASSERT(*isOwnProperty == (receivers.empty() && convertUnboxedGroups.empty())); return true; }
ObjectGroup* BaselineInspector::getTemplateObjectGroup(jsbytecode* pc) { if (!hasICScript()) { return nullptr; } const ICEntry& entry = icEntryFromPC(pc); for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { switch (stub->kind()) { case ICStub::NewArray_Fallback: return stub->toNewArray_Fallback()->templateGroup(); default: break; } } return nullptr; }
JSObject * BaselineInspector::getTemplateObject(jsbytecode *pc) { if (!hasBaselineScript()) return nullptr; const ICEntry &entry = icEntryFromPC(pc); for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { switch (stub->kind()) { case ICStub::NewArray_Fallback: return stub->toNewArray_Fallback()->templateObject(); case ICStub::NewObject_Fallback: return stub->toNewObject_Fallback()->templateObject(); case ICStub::Rest_Fallback: return stub->toRest_Fallback()->templateObject(); case ICStub::Call_Scripted: if (JSObject *obj = stub->toCall_Scripted()->templateObject()) return obj; break; default: break; } } return nullptr; }
MIRType BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc) { if (!hasICScript()) { return MIRType::Value; } const ICEntry& entry = icEntryFromPC(pc); MIRType type = MIRType::None; for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { MIRType stubType = MIRType::None; if (stub->isCacheIR_Monitored()) { stubType = GetCacheIRExpectedInputType(stub->toCacheIR_Monitored()); if (stubType == MIRType::Value) { return MIRType::Value; } } else if (stub->isGetElem_Fallback() || stub->isGetProp_Fallback()) { // If we have an unoptimizable access, don't try to optimize. if (stub->toFallbackStub()->state().hasFailures()) { return MIRType::Value; } } else { MOZ_CRASH("Unexpected stub"); } if (type != MIRType::None) { if (type != stubType) { return MIRType::Value; } } else { type = stubType; } } return (type == MIRType::None) ? MIRType::Value : type; }
bool BaselineInspector::megamorphicGetterSetterFunction( jsbytecode* pc, bool isGetter, JSFunction** getterOrSetter) { if (!hasICScript()) { return false; } *getterOrSetter = nullptr; const ICEntry& entry = icEntryFromPC(pc); for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isCacheIR_Monitored()) { MOZ_ASSERT(isGetter); JSFunction* getter = GetMegamorphicGetterSetterFunction( stub, stub->toCacheIR_Monitored()->stubInfo(), isGetter); if (!getter || (*getterOrSetter && *getterOrSetter != getter)) { return false; } *getterOrSetter = getter; continue; } if (stub->isCacheIR_Updated()) { MOZ_ASSERT(!isGetter); JSFunction* setter = GetMegamorphicGetterSetterFunction( stub, stub->toCacheIR_Updated()->stubInfo(), isGetter); if (!setter || (*getterOrSetter && *getterOrSetter != setter)) { return false; } *getterOrSetter = setter; continue; } if (stub->isFallback()) { if (stub->toFallbackStub()->state().hasFailures()) { return false; } if (stub->toFallbackStub()->state().mode() != ICState::Mode::Megamorphic) { return false; } continue; } return false; } if (!*getterOrSetter) { return false; } return true; }
JSObject* BaselineInspector::getTemplateObjectForSimdCtor(jsbytecode* pc, SimdType simdType) { if (!hasBaselineScript()) return nullptr; const ICEntry& entry = icEntryFromPC(pc); for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isCall_ClassHook() && stub->toCall_ClassHook()->clasp() == &SimdTypeDescr::class_) { JSObject* templateObj = stub->toCall_ClassHook()->templateObject(); InlineTypedObject& typedObj = templateObj->as<InlineTypedObject>(); if (typedObj.typeDescr().as<SimdTypeDescr>().type() == simdType) return templateObj; } } return nullptr; }
MIRType BaselineInspector::expectedResultType(jsbytecode* pc) { // Look at the IC entries for this op to guess what type it will produce, // returning MIRType::None otherwise. Note that IonBuilder may call this // for bytecode ops that are unreachable and don't have a Baseline IC, see // comment in monomorphicStub. ICStub* stub = monomorphicStub(pc); if (!stub) { return MIRType::None; } switch (stub->kind()) { case ICStub::CacheIR_Regular: return ParseCacheIRStub(stub); default: return MIRType::None; } }
JSFunction* BaselineInspector::getSingleCallee(jsbytecode* pc) { MOZ_ASSERT(*pc == JSOP_NEW); if (!hasBaselineScript()) return nullptr; const ICEntry& entry = icEntryFromPC(pc); ICStub* stub = entry.firstStub(); if (entry.fallbackStub()->toCall_Fallback()->hadUnoptimizableCall()) return nullptr; if (!stub->isCall_Scripted() || stub->next() != entry.fallbackStub()) return nullptr; return stub->toCall_Scripted()->callee(); }
JSFunction* BaselineInspector::getSingleCallee(jsbytecode* pc) { MOZ_ASSERT(*pc == JSOP_NEW); if (!hasICScript()) { return nullptr; } const ICEntry& entry = icEntryFromPC(pc); ICStub* stub = entry.firstStub(); if (entry.fallbackStub()->state().hasFailures()) { return nullptr; } if (!stub->isCall_Scripted() || stub->next() != entry.fallbackStub()) { return nullptr; } return stub->toCall_Scripted()->callee(); }
ICStub* BaselineInspector::monomorphicStub(jsbytecode* pc) { if (!hasBaselineScript()) return nullptr; // IonBuilder::analyzeNewLoopTypes may call this (via expectedResultType // below) on code that's unreachable, according to BytecodeAnalysis. Use // maybeICEntryFromPC to handle this. const ICEntry* entry = maybeICEntryFromPC(pc); if (!entry) return nullptr; ICStub* stub = entry->firstStub(); ICStub* next = stub->next(); if (!next || !next->isFallback()) return nullptr; return stub; }
JSObject * BaselineInspector::commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter) { if (!hasBaselineScript()) return nullptr; const ICEntry &entry = icEntryFromPC(pc); JSObject *holder = nullptr; Shape *holderShape = nullptr; JSFunction *setter = nullptr; for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) { ICSetPropCallSetter *nstub = static_cast<ICSetPropCallSetter *>(stub); if (!holder) { holder = nstub->holder(); holderShape = nstub->holderShape(); setter = nstub->setter(); } else if (nstub->holderShape() != holderShape) { return nullptr; } else { MOZ_ASSERT(setter == nstub->setter()); } } else if (stub->isSetProp_Fallback() && stub->toSetProp_Fallback()->hadUnoptimizableAccess()) { // We have an unoptimizable access, so don't try to optimize. return nullptr; } } *lastProperty = holderShape; *commonSetter = setter; return holder; }
MCompare::CompareType BaselineInspector::expectedCompareType(jsbytecode* pc) { ICStub* first = monomorphicStub(pc); ICStub* second = nullptr; if (!first && !dimorphicStub(pc, &first, &second)) { return MCompare::Compare_Unknown; } if (ICStub* fallback = second ? second->next() : first->next()) { MOZ_ASSERT(fallback->isFallback()); if (fallback->toFallbackStub()->state().hasFailures()) { return MCompare::Compare_Unknown; } } MCompare::CompareType first_type = ParseCacheIRStubForCompareType(first->toCacheIR_Regular()); if (!second) { return first_type; } MCompare::CompareType second_type = ParseCacheIRStubForCompareType(second->toCacheIR_Regular()); if (first_type == MCompare::Compare_Unknown || second_type == MCompare::Compare_Unknown) { return MCompare::Compare_Unknown; } if (first_type == second_type) { return first_type; } return CompatibleType(first_type, second_type); }
bool BaselineInspector::commonSetPropFunction( jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonSetter, bool* isOwnProperty, ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups) { if (!hasICScript()) { return false; } MOZ_ASSERT(receivers.empty()); MOZ_ASSERT(convertUnboxedGroups.empty()); *commonSetter = nullptr; const ICEntry& entry = icEntryFromPC(pc); for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isCacheIR_Updated()) { if (!AddCacheIRSetPropFunction(stub->toCacheIR_Updated(), holder, holderShape, commonSetter, isOwnProperty, receivers, convertUnboxedGroups)) { return false; } } else if (!stub->isFallback() || stub->toFallbackStub()->state().hasFailures()) { // We have an unoptimizable access, so don't try to optimize. return false; } } if (!*commonSetter) { return false; } MOZ_ASSERT(*isOwnProperty == !*holder); return true; }
bool BaselineInspector::isOptimizableConstStringSplit(jsbytecode* pc, JSString** strOut, JSString** sepOut, JSObject** objOut) { if (!hasBaselineScript()) return false; const ICEntry& entry = icEntryFromPC(pc); // If ConstStringSplit stub is attached, must have only one stub attached. if (entry.fallbackStub()->numOptimizedStubs() != 1) return false; ICStub* stub = entry.firstStub(); if (stub->kind() != ICStub::Call_ConstStringSplit) return false; *strOut = stub->toCall_ConstStringSplit()->expectedStr(); *sepOut = stub->toCall_ConstStringSplit()->expectedSep(); *objOut = stub->toCall_ConstStringSplit()->templateObject(); return true; }
bool BaselineInspector::commonGetPropFunction(jsbytecode* pc, bool innerized, JSObject** holder, Shape** holderShape, JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty, ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups) { if (!hasBaselineScript()) return false; MOZ_ASSERT(receivers.empty()); MOZ_ASSERT(convertUnboxedGroups.empty()); *globalShape = nullptr; *commonGetter = nullptr; const ICEntry& entry = icEntryFromPC(pc); for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isCacheIR_Monitored()) { if (!AddCacheIRGetPropFunction(stub->toCacheIR_Monitored(), innerized, holder, holderShape, commonGetter, globalShape, isOwnProperty, receivers, convertUnboxedGroups, script)) { return false; } } else if (stub->isGetProp_Fallback()) { // If we have an unoptimizable access, don't try to optimize. if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) return false; } else if (stub->isGetName_Fallback()) { if (stub->toGetName_Fallback()->hadUnoptimizableAccess()) return false; } else { return false; } } if (!*commonGetter) return false; MOZ_ASSERT(*isOwnProperty == !*holder); MOZ_ASSERT(*isOwnProperty == (receivers.empty() && convertUnboxedGroups.empty())); return true; }
MCompare::CompareType BaselineInspector::expectedCompareType(jsbytecode *pc) { ICStub *first = monomorphicStub(pc), *second = nullptr; if (!first && !dimorphicStub(pc, &first, &second)) return MCompare::Compare_Unknown; if (ICStub *fallback = second ? second->next() : first->next()) { MOZ_ASSERT(fallback->isFallback()); if (fallback->toCompare_Fallback()->hadUnoptimizableAccess()) return MCompare::Compare_Unknown; } if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind()))) { ICCompare_Int32WithBoolean *coerce = first->isCompare_Int32WithBoolean() ? first->toCompare_Int32WithBoolean() : ((second && second->isCompare_Int32WithBoolean()) ? second->toCompare_Int32WithBoolean() : nullptr); if (coerce) { return coerce->lhsIsInt32() ? MCompare::Compare_Int32MaybeCoerceRHS : MCompare::Compare_Int32MaybeCoerceLHS; } return MCompare::Compare_Int32; } if (CanUseDoubleCompare(first->kind()) && (!second || CanUseDoubleCompare(second->kind()))) { ICCompare_NumberWithUndefined *coerce = first->isCompare_NumberWithUndefined() ? first->toCompare_NumberWithUndefined() : (second && second->isCompare_NumberWithUndefined()) ? second->toCompare_NumberWithUndefined() : nullptr; if (coerce) { return coerce->lhsIsUndefined() ? MCompare::Compare_DoubleMaybeCoerceLHS : MCompare::Compare_DoubleMaybeCoerceRHS; } return MCompare::Compare_Double; } return MCompare::Compare_Unknown; }
bool BaselineInspector::maybeInfoForProtoReadSlot( jsbytecode* pc, ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups, JSObject** holder) { // This is like maybeInfoForPropertyOp, but for when the property exists on // the prototype. MOZ_ASSERT(receivers.empty()); MOZ_ASSERT(convertUnboxedGroups.empty()); MOZ_ASSERT(!*holder); if (!hasICScript()) { return true; } MOZ_ASSERT(isValidPC(pc)); const ICEntry& entry = icEntryFromPC(pc); ICStub* stub = entry.firstStub(); while (stub->next()) { ReceiverGuard receiver; if (stub->isCacheIR_Monitored()) { if (!GetCacheIRReceiverForProtoReadSlot(stub->toCacheIR_Monitored(), &receiver, holder)) { receivers.clear(); return true; } } else { receivers.clear(); return true; } if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) { return false; } stub = stub->next(); } if (stub->toFallbackStub()->state().hasFailures()) { receivers.clear(); } // Don't inline if there are more than 5 receivers. if (receivers.length() > 5) { receivers.clear(); } MOZ_ASSERT_IF(!receivers.empty(), *holder); return true; }
bool BaselineInspector::hasSeenDoubleResult(jsbytecode* pc) { if (!hasICScript()) { return false; } const ICEntry& entry = icEntryFromPC(pc); ICStub* stub = entry.fallbackStub(); MOZ_ASSERT(stub->isUnaryArith_Fallback() || stub->isBinaryArith_Fallback()); if (stub->isUnaryArith_Fallback()) { return stub->toUnaryArith_Fallback()->sawDoubleResult(); } return stub->toBinaryArith_Fallback()->sawDoubleResult(); }
bool BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonSetter, bool* isOwnProperty, ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups) { if (!hasBaselineScript()) return false; MOZ_ASSERT(receivers.empty()); MOZ_ASSERT(convertUnboxedGroups.empty()); *holder = nullptr; const ICEntry& entry = icEntryFromPC(pc); for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) { ICSetPropCallSetter* nstub = static_cast<ICSetPropCallSetter*>(stub); if (!AddReceiver(nstub->guard(), receivers, convertUnboxedGroups)) return false; if (!*holder) { *holder = nstub->holder(); *holderShape = nstub->holderShape(); *commonSetter = nstub->setter(); *isOwnProperty = false; } else if (nstub->holderShape() != *holderShape) { return false; } else { MOZ_ASSERT(*commonSetter == nstub->setter()); } } else if (!stub->isSetProp_Fallback() || stub->toSetProp_Fallback()->hadUnoptimizableAccess()) { // We have an unoptimizable access, so don't try to optimize. return false; } } if (!*holder) return false; return true; }
bool BaselineInspector::dimorphicStub(jsbytecode *pc, ICStub **pfirst, ICStub **psecond) { if (!hasBaselineScript()) return false; const ICEntry &entry = icEntryFromPC(pc); ICStub *stub = entry.firstStub(); ICStub *next = stub->next(); ICStub *after = next ? next->next() : NULL; if (!after || !after->isFallback()) return false; *pfirst = stub; *psecond = next; return true; }