RawShape BaselineInspector::maybeMonomorphicShapeForPropertyOp(jsbytecode *pc) { if (!hasBaselineScript()) return NULL; JS_ASSERT(isValidPC(pc)); const ICEntry &entry = icEntryFromPC(pc); ICStub *stub = entry.firstStub(); ICStub *next = stub->next(); if (!next || !next->isFallback()) return NULL; if (stub->isGetProp_Native()) { JS_ASSERT(next->isGetProp_Fallback()); if (next->toGetProp_Fallback()->hadUnoptimizableAccess()) return NULL; return stub->toGetProp_Native()->shape(); } if (stub->isSetProp_Native()) { JS_ASSERT(next->isSetProp_Fallback()); if (next->toSetProp_Fallback()->hadUnoptimizableAccess()) return NULL; return stub->toSetProp_Native()->shape(); } return NULL; }
bool BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups) { // Return a list of the receivers seen by the baseline IC for the current // op. Empty lists indicate no receivers are known, or there was an // uncacheable access. convertUnboxedGroups is used for unboxed object // groups which have been seen, but have had instances converted to native // objects and should be eagerly converted by Ion. MOZ_ASSERT(receivers.empty()); MOZ_ASSERT(convertUnboxedGroups.empty()); if (!hasBaselineScript()) 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 (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(), &receiver) && !GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Monitored(), &receiver)) { receivers.clear(); return true; } } else if (stub->isCacheIR_Updated()) { if (!GetCacheIRReceiverForNativeSetSlot(stub->toCacheIR_Updated(), &receiver) && !GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Updated(), &receiver)) { receivers.clear(); return true; } } else { receivers.clear(); return true; } if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) return false; stub = stub->next(); } if (stub->isGetProp_Fallback()) { if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) receivers.clear(); } else { if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) receivers.clear(); } // Don't inline if there are more than 5 receivers. if (receivers.length() > 5) receivers.clear(); return true; }
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::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() || stub->isGetProp_CallNativeGlobal()) { 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; }
bool BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, Vector<Shape *> &shapes) { // Return a list of shapes seen by the baseline IC for the current op. // An empty list indicates no shapes are known, or there was an uncacheable // access. JS_ASSERT(shapes.empty()); if (!hasBaselineScript()) return true; JS_ASSERT(isValidPC(pc)); const ICEntry &entry = icEntryFromPC(pc); ICStub *stub = entry.firstStub(); while (stub->next()) { Shape *shape; if (stub->isGetProp_Native()) { shape = stub->toGetProp_Native()->shape(); } else if (stub->isSetProp_Native()) { shape = stub->toSetProp_Native()->shape(); } else { shapes.clear(); return true; } // Don't add the same shape twice (this can happen if there are multiple // SetProp_Native stubs with different TypeObject's). bool found = false; for (size_t i = 0; i < shapes.length(); i++) { if (shapes[i] == shape) { found = true; break; } } if (!found && !shapes.append(shape)) return false; stub = stub->next(); } if (stub->isGetProp_Fallback()) { if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) shapes.clear(); } else { if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) shapes.clear(); } // Don't inline if there are more than 5 shapes. if (shapes.length() > 5) shapes.clear(); return true; }
bool BaselineInspector::megamorphicGetterSetterFunction(jsbytecode* pc, bool isGetter, JSFunction** getterOrSetter) { if (!hasBaselineScript()) 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->isGetProp_Fallback()) { if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) return false; if (stub->toGetProp_Fallback()->state().mode() != ICState::Mode::Megamorphic) return false; continue; } if (stub->isSetProp_Fallback()) { if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) return false; if (stub->toSetProp_Fallback()->state().mode() != ICState::Mode::Megamorphic) return false; continue; } return false; } if (!*getterOrSetter) return false; return true; }
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::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; }
JSObject * BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter, Shape **globalShape) { if (!hasBaselineScript()) return nullptr; const ICEntry &entry = icEntryFromPC(pc); JSObject* holder = nullptr; Shape *holderShape = nullptr; JSFunction *getter = nullptr; Shape *global = nullptr; for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isGetProp_CallScripted() || stub->isGetProp_CallNative() || stub->isGetProp_CallNativePrototype()) { ICGetPropCallGetter *nstub = static_cast<ICGetPropCallGetter *>(stub); if (!holder) { holder = nstub->holder(); holderShape = nstub->holderShape(); getter = nstub->getter(); global = GlobalShapeForGetPropFunction(nstub); } else if (nstub->holderShape() != holderShape || GlobalShapeForGetPropFunction(nstub) != global) { return nullptr; } else { MOZ_ASSERT(getter == nstub->getter()); } } else if (stub->isGetProp_Fallback() && stub->toGetProp_Fallback()->hadUnoptimizableAccess()) { // We have an unoptimizable access, so don't try to optimize. return nullptr; } } *lastProperty = holderShape; *commonGetter = getter; *globalShape = global; return holder; }
bool BaselineInspector::maybeInfoForPropertyOp(jsbytecode *pc, ShapeVector &nativeShapes, ObjectGroupVector &unboxedGroups, ObjectGroupVector &convertUnboxedGroups) { // Return lists of native shapes and unboxed objects seen by the baseline // IC for the current op. Empty lists indicate no shapes/types are known, // or there was an uncacheable access. convertUnboxedGroups is used for // unboxed object groups which have been seen, but have had instances // converted to native objects and should be eagerly converted by Ion. MOZ_ASSERT(nativeShapes.empty()); MOZ_ASSERT(unboxedGroups.empty()); MOZ_ASSERT(convertUnboxedGroups.empty()); if (!hasBaselineScript()) return true; MOZ_ASSERT(isValidPC(pc)); const ICEntry &entry = icEntryFromPC(pc); ICStub *stub = entry.firstStub(); while (stub->next()) { Shape *shape = nullptr; ObjectGroup *group = nullptr; if (stub->isGetProp_Native()) { shape = stub->toGetProp_Native()->shape(); } else if (stub->isSetProp_Native()) { shape = stub->toSetProp_Native()->shape(); } else if (stub->isGetProp_Unboxed()) { group = stub->toGetProp_Unboxed()->group(); } else if (stub->isSetProp_Unboxed()) { group = stub->toSetProp_Unboxed()->group(); } else { nativeShapes.clear(); unboxedGroups.clear(); return true; } if (group && group->unboxedLayout().nativeGroup()) { if (!VectorAppendNoDuplicate(convertUnboxedGroups, group)) return false; shape = group->unboxedLayout().nativeShape(); group = nullptr; } if (shape) { if (!VectorAppendNoDuplicate(nativeShapes, shape)) return false; } else { if (!VectorAppendNoDuplicate(unboxedGroups, group)) return false; } stub = stub->next(); } if (stub->isGetProp_Fallback()) { if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) { nativeShapes.clear(); unboxedGroups.clear(); } } else { if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) { nativeShapes.clear(); unboxedGroups.clear(); } } // Don't inline if there are more than 5 shapes/groups. if (nativeShapes.length() + unboxedGroups.length() > 5) { nativeShapes.clear(); unboxedGroups.clear(); } return true; }