Bug 1505574 - Remove initial chunk of Unboxed Objects machinery r=iain

Differential Revision: https://phabricator.services.mozilla.com/D24037

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Matthew Gaudet 2019-03-22 16:00:12 +00:00
Родитель b31262c6aa
Коммит 6d1bcb4f0f
10 изменённых файлов: 38 добавлений и 489 удалений

Просмотреть файл

@ -60,16 +60,7 @@ static bool VectorAppendNoDuplicate(S& list, T value) {
}
static bool AddReceiver(
const ReceiverGuard& receiver, BaselineInspector::ReceiverVector& receivers,
BaselineInspector::ObjectGroupVector& convertUnboxedGroups) {
if (receiver.group) {
AutoSweepObjectGroup sweep(receiver.group);
if (auto* layout = receiver.group->maybeUnboxedLayout(sweep)) {
if (layout->nativeGroup()) {
return VectorAppendNoDuplicate(convertUnboxedGroups, receiver.group);
}
}
}
const ReceiverGuard& receiver, BaselineInspector::ReceiverVector& receivers) {
return VectorAppendNoDuplicate(receivers, receiver);
}
@ -255,15 +246,11 @@ ICEntry* BaselineInspector::maybeICEntryFromPC(jsbytecode* pc) {
}
bool BaselineInspector::maybeInfoForPropertyOp(
jsbytecode* pc, ReceiverVector& receivers,
ObjectGroupVector& convertUnboxedGroups) {
jsbytecode* pc, ReceiverVector& receivers) {
// 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.
// uncacheable access.
MOZ_ASSERT(receivers.empty());
MOZ_ASSERT(convertUnboxedGroups.empty());
if (!hasICScript()) {
return true;
@ -296,7 +283,7 @@ bool BaselineInspector::maybeInfoForPropertyOp(
return true;
}
if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) {
if (!AddReceiver(receiver, receivers)) {
return false;
}
@ -1089,7 +1076,6 @@ static bool AddCacheIRGlobalGetter(
ICCacheIR_Monitored* stub, bool innerized, JSObject** holder_,
Shape** holderShape_, JSFunction** commonGetter, Shape** globalShape_,
bool* isOwnProperty, BaselineInspector::ReceiverVector& receivers,
BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
JSScript* script) {
// We are matching on the IR generated by tryAttachGlobalNameGetter:
//
@ -1155,7 +1141,7 @@ static bool AddCacheIRGlobalGetter(
ReceiverGuard receiver;
receiver.shape = globalLexicalShape;
if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) {
if (!AddReceiver(receiver, receivers)) {
return false;
}
@ -1216,7 +1202,6 @@ static bool AddCacheIRGetPropFunction(
ICCacheIR_Monitored* stub, jsid id, bool innerized, JSObject** holder,
Shape** holderShape, JSFunction** commonGetter, Shape** globalShape,
bool* isOwnProperty, BaselineInspector::ReceiverVector& receivers,
BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
JSScript* script) {
// We match either an own getter:
//
@ -1255,7 +1240,7 @@ static bool AddCacheIRGetPropFunction(
if (!reader.matchOp(CacheOp::GuardIsObject, objId)) {
return AddCacheIRGlobalGetter(stub, innerized, holder, holderShape,
commonGetter, globalShape, isOwnProperty,
receivers, convertUnboxedGroups, script);
receivers, script);
}
if (!JSID_IS_EMPTY(id)) {
@ -1358,7 +1343,7 @@ static bool AddCacheIRGetPropFunction(
return true;
}
if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) {
if (!AddReceiver(receiver, receivers)) {
return false;
}
@ -1372,15 +1357,13 @@ static bool AddCacheIRGetPropFunction(
bool BaselineInspector::commonGetPropFunction(
jsbytecode* pc, jsid id, bool innerized, JSObject** holder,
Shape** holderShape, JSFunction** commonGetter, Shape** globalShape,
bool* isOwnProperty, ReceiverVector& receivers,
ObjectGroupVector& convertUnboxedGroups) {
bool* isOwnProperty, ReceiverVector& receivers) {
if (!hasICScript()) {
return false;
}
MOZ_ASSERT(IsGetPropPC(pc) || IsGetElemPC(pc) || JSOp(*pc) == JSOP_GETGNAME);
MOZ_ASSERT(receivers.empty());
MOZ_ASSERT(convertUnboxedGroups.empty());
// Only GetElem operations need to guard against a specific property id.
if (!IsGetElemPC(pc)) {
@ -1395,8 +1378,7 @@ bool BaselineInspector::commonGetPropFunction(
if (stub->isCacheIR_Monitored()) {
if (!AddCacheIRGetPropFunction(stub->toCacheIR_Monitored(), id, innerized,
holder, holderShape, commonGetter,
globalShape, isOwnProperty, receivers,
convertUnboxedGroups, script)) {
globalShape, isOwnProperty, receivers, script)) {
return false;
}
} else if (stub->isFallback()) {
@ -1414,8 +1396,7 @@ bool BaselineInspector::commonGetPropFunction(
}
MOZ_ASSERT(*isOwnProperty == !*holder);
MOZ_ASSERT(*isOwnProperty ==
(receivers.empty() && convertUnboxedGroups.empty()));
MOZ_ASSERT(*isOwnProperty == receivers.empty());
return true;
}
@ -1521,8 +1502,7 @@ bool BaselineInspector::megamorphicGetterSetterFunction(
static bool AddCacheIRSetPropFunction(
ICCacheIR_Updated* stub, JSObject** holder, Shape** holderShape,
JSFunction** commonSetter, bool* isOwnProperty,
BaselineInspector::ReceiverVector& receivers,
BaselineInspector::ObjectGroupVector& convertUnboxedGroups) {
BaselineInspector::ReceiverVector& receivers) {
// We match either an own setter:
//
// GuardIsObject objId
@ -1608,7 +1588,7 @@ static bool AddCacheIRSetPropFunction(
return true;
}
if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) {
if (!AddReceiver(receiver, receivers)) {
return false;
}
@ -1621,8 +1601,7 @@ static bool AddCacheIRSetPropFunction(
bool BaselineInspector::commonSetPropFunction(
jsbytecode* pc, JSObject** holder, Shape** holderShape,
JSFunction** commonSetter, bool* isOwnProperty, ReceiverVector& receivers,
ObjectGroupVector& convertUnboxedGroups) {
JSFunction** commonSetter, bool* isOwnProperty, ReceiverVector& receivers) {
if (!hasICScript()) {
return false;
}
@ -1631,7 +1610,6 @@ bool BaselineInspector::commonSetPropFunction(
JSOp(*pc) == JSOP_INITPROP || JSOp(*pc) == JSOP_INITLOCKEDPROP ||
JSOp(*pc) == JSOP_INITHIDDENPROP);
MOZ_ASSERT(receivers.empty());
MOZ_ASSERT(convertUnboxedGroups.empty());
*commonSetter = nullptr;
const ICEntry& entry = icEntryFromPC(pc);
@ -1640,7 +1618,7 @@ bool BaselineInspector::commonSetPropFunction(
if (stub->isCacheIR_Updated()) {
if (!AddCacheIRSetPropFunction(stub->toCacheIR_Updated(), holder,
holderShape, commonSetter, isOwnProperty,
receivers, convertUnboxedGroups)) {
receivers)) {
return false;
}
} else if (!stub->isFallback() ||
@ -1716,13 +1694,11 @@ static bool GetCacheIRReceiverForProtoReadSlot(ICCacheIR_Monitored* stub,
}
bool BaselineInspector::maybeInfoForProtoReadSlot(
jsbytecode* pc, ReceiverVector& receivers,
ObjectGroupVector& convertUnboxedGroups, JSObject** holder) {
jsbytecode* pc, ReceiverVector& receivers, 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()) {
@ -1746,7 +1722,7 @@ bool BaselineInspector::maybeInfoForProtoReadSlot(
return true;
}
if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) {
if (!AddReceiver(receiver, receivers)) {
return false;
}

Просмотреть файл

@ -78,12 +78,10 @@ class BaselineInspector {
typedef Vector<ReceiverGuard, 4, JitAllocPolicy> ReceiverVector;
typedef Vector<ObjectGroup*, 4, JitAllocPolicy> ObjectGroupVector;
MOZ_MUST_USE bool maybeInfoForPropertyOp(
jsbytecode* pc, ReceiverVector& receivers,
ObjectGroupVector& convertUnboxedGroups);
jsbytecode* pc, ReceiverVector& receivers);
MOZ_MUST_USE bool maybeInfoForProtoReadSlot(
jsbytecode* pc, ReceiverVector& receivers,
ObjectGroupVector& convertUnboxedGroups, JSObject** holder);
jsbytecode* pc, ReceiverVector& receivers, JSObject** holder);
SetElemICInspector setElemICInspector(jsbytecode* pc) {
return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback);
@ -123,16 +121,14 @@ class BaselineInspector {
MOZ_MUST_USE bool commonGetPropFunction(
jsbytecode* pc, jsid id, bool innerized, JSObject** holder,
Shape** holderShape, JSFunction** commonGetter, Shape** globalShape,
bool* isOwnProperty, ReceiverVector& receivers,
ObjectGroupVector& convertUnboxedGroups);
bool* isOwnProperty, ReceiverVector& receivers);
MOZ_MUST_USE bool megamorphicGetterSetterFunction(
jsbytecode* pc, jsid id, bool isGetter, JSFunction** getterOrSetter);
MOZ_MUST_USE bool commonSetPropFunction(
jsbytecode* pc, JSObject** holder, Shape** holderShape,
JSFunction** commonSetter, bool* isOwnProperty, ReceiverVector& receivers,
ObjectGroupVector& convertUnboxedGroups);
JSFunction** commonSetter, bool* isOwnProperty, ReceiverVector& receivers);
MOZ_MUST_USE bool instanceOfData(jsbytecode* pc, Shape** shape,
uint32_t* slot, JSObject** prototypeObject);

Просмотреть файл

@ -8459,9 +8459,6 @@ AbortReasonOr<Ok> IonBuilder::jsop_getelem() {
}
obj = maybeUnboxForPropertyAccess(obj);
if (obj->type() == MIRType::Object) {
obj = convertUnboxedObjects(obj);
}
if (!forceInlineCaches()) {
// Note: no trackOptimizationAttempt call is needed, getElemTryGetProp
@ -9535,7 +9532,7 @@ AbortReasonOr<Ok> IonBuilder::jsop_setelem() {
MDefinition* value = current->pop();
MDefinition* index = current->pop();
MDefinition* object = convertUnboxedObjects(current->pop());
MDefinition* object = current->pop();
trackTypeInfo(TrackedTypeSite::Receiver, object->type(),
object->resultTypeSet());
@ -10954,9 +10951,6 @@ AbortReasonOr<Ok> IonBuilder::jsop_getprop(PropertyName* name) {
}
obj = maybeUnboxForPropertyAccess(obj);
if (obj->type() == MIRType::Object) {
obj = convertUnboxedObjects(obj);
}
BarrierKind barrier = PropertyReadNeedsTypeBarrier(
analysisContext, alloc(), constraints(), obj, name, types);
@ -11401,50 +11395,6 @@ AbortReasonOr<Ok> IonBuilder::getPropTryComplexPropOfTypedObject(
fieldTypeObj);
}
MDefinition* IonBuilder::convertUnboxedObjects(MDefinition* obj) {
// If obj might be in any particular unboxed group which should be
// converted to a native representation, perform that conversion. This does
// not guarantee the object will not have such a group afterwards, if the
// object's possible groups are not precisely known.
TemporaryTypeSet* types = obj->resultTypeSet();
if (!types || types->unknownObject() || !types->objectOrSentinel()) {
return obj;
}
BaselineInspector::ObjectGroupVector list(alloc());
for (size_t i = 0; i < types->getObjectCount(); i++) {
TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i);
if (!key || !key->isGroup()) {
continue;
}
AutoSweepObjectGroup sweep(key->group());
if (UnboxedLayout* layout = key->group()->maybeUnboxedLayout(sweep)) {
AutoEnterOOMUnsafeRegion oomUnsafe;
if (layout->nativeGroup() && !list.append(key->group())) {
oomUnsafe.crash("IonBuilder::convertUnboxedObjects");
}
}
}
return convertUnboxedObjects(obj, list);
}
MDefinition* IonBuilder::convertUnboxedObjects(
MDefinition* obj, const BaselineInspector::ObjectGroupVector& list) {
for (size_t i = 0; i < list.length(); i++) {
ObjectGroup* group = list[i];
if (TemporaryTypeSet* types = obj->resultTypeSet()) {
if (!types->hasType(TypeSet::ObjectType(group))) {
continue;
}
}
obj = MConvertUnboxedObjectToNative::New(alloc(), obj, group);
current->add(obj->toInstruction());
}
return obj;
}
AbortReasonOr<Ok> IonBuilder::getPropTryDefiniteSlot(bool* emitted,
MDefinition* obj,
PropertyName* name,
@ -11637,13 +11587,10 @@ AbortReasonOr<Ok> IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj,
MDefinition* IonBuilder::addShapeGuardsForGetterSetter(
MDefinition* obj, JSObject* holder, Shape* holderShape,
const BaselineInspector::ReceiverVector& receivers,
const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
bool isOwnProperty) {
MOZ_ASSERT(isOwnProperty == !holder);
MOZ_ASSERT(holderShape);
obj = convertUnboxedObjects(obj, convertUnboxedGroups);
if (isOwnProperty) {
MOZ_ASSERT(receivers.empty());
return addShapeGuard(obj, holderShape, Bailout_ShapeGuard);
@ -11673,10 +11620,9 @@ AbortReasonOr<Ok> IonBuilder::getPropTryCommonGetter(bool* emitted,
JSObject* foundProto = nullptr;
bool isOwnProperty = false;
BaselineInspector::ReceiverVector receivers(alloc());
BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
if (inspector->commonGetPropFunction(
pc, id, innerized, &foundProto, &lastProperty, &commonGetter,
&globalShape, &isOwnProperty, receivers, convertUnboxedGroups)) {
&globalShape, &isOwnProperty, receivers)) {
bool canUseTIForGetter = false;
if (!isOwnProperty) {
// If it's not an own property, try to use TI to avoid shape guards.
@ -11690,7 +11636,7 @@ AbortReasonOr<Ok> IonBuilder::getPropTryCommonGetter(bool* emitted,
// If it's an own property or type information is bad, we can still
// optimize the getter if we shape guard.
obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
receivers, convertUnboxedGroups,
receivers,
isOwnProperty);
if (!obj) {
return abort(AbortReason::Alloc);
@ -11890,8 +11836,7 @@ AbortReasonOr<Ok> IonBuilder::getPropTryInlineAccess(bool* emitted,
MOZ_ASSERT(*emitted == false);
BaselineInspector::ReceiverVector receivers(alloc());
BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) {
if (!inspector->maybeInfoForPropertyOp(pc, receivers)) {
return abort(AbortReason::Alloc);
}
@ -11899,8 +11844,6 @@ AbortReasonOr<Ok> IonBuilder::getPropTryInlineAccess(bool* emitted,
return Ok();
}
obj = convertUnboxedObjects(obj, convertUnboxedGroups);
MIRType rvalType = types->getKnownMIRType();
if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType)) {
rvalType = MIRType::Value;
@ -12020,9 +11963,8 @@ AbortReasonOr<Ok> IonBuilder::getPropTryInlineProtoAccess(
MOZ_ASSERT(*emitted == false);
BaselineInspector::ReceiverVector receivers(alloc());
BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
JSObject* holder = nullptr;
if (!inspector->maybeInfoForProtoReadSlot(pc, receivers, convertUnboxedGroups,
if (!inspector->maybeInfoForProtoReadSlot(pc, receivers,
&holder)) {
return abort(AbortReason::Alloc);
}
@ -12044,7 +11986,6 @@ AbortReasonOr<Ok> IonBuilder::getPropTryInlineProtoAccess(
}
// Guard on the receiver shapes/groups.
obj = convertUnboxedObjects(obj, convertUnboxedGroups);
obj = addGuardReceiverPolymorphic(obj, receivers);
if (!obj) {
return abort(AbortReason::Alloc);
@ -12257,7 +12198,7 @@ AbortReasonOr<Ok> IonBuilder::getPropTryInnerize(bool* emitted,
AbortReasonOr<Ok> IonBuilder::jsop_setprop(PropertyName* name) {
MDefinition* value = current->pop();
MDefinition* obj = convertUnboxedObjects(current->pop());
MDefinition* obj = current->pop();
bool emitted = false;
startTrackingOptimizations();
@ -12344,10 +12285,9 @@ AbortReasonOr<Ok> IonBuilder::setPropTryCommonSetter(bool* emitted,
JSObject* foundProto = nullptr;
bool isOwnProperty;
BaselineInspector::ReceiverVector receivers(alloc());
BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
if (inspector->commonSetPropFunction(pc, &foundProto, &lastProperty,
&commonSetter, &isOwnProperty,
receivers, convertUnboxedGroups)) {
receivers)) {
bool canUseTIForSetter = false;
if (!isOwnProperty) {
// If it's not an own property, try to use TI to avoid shape guards.
@ -12361,8 +12301,7 @@ AbortReasonOr<Ok> IonBuilder::setPropTryCommonSetter(bool* emitted,
// If it's an own property or type information is bad, we can still
// optimize the setter if we shape guard.
obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
receivers, convertUnboxedGroups,
isOwnProperty);
receivers, isOwnProperty);
if (!obj) {
return abort(AbortReason::Alloc);
}
@ -12750,8 +12689,7 @@ AbortReasonOr<Ok> IonBuilder::setPropTryInlineAccess(
}
BaselineInspector::ReceiverVector receivers(alloc());
BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) {
if (!inspector->maybeInfoForPropertyOp(pc, receivers)) {
return abort(AbortReason::Alloc);
}
@ -12759,8 +12697,6 @@ AbortReasonOr<Ok> IonBuilder::setPropTryInlineAccess(
return Ok();
}
obj = convertUnboxedObjects(obj, convertUnboxedGroups);
if (receivers.length() == 1) {
if (!receivers[0].group) {
// Monomorphic store to a native object.
@ -13406,7 +13342,7 @@ AbortReasonOr<Ok> IonBuilder::jsop_setaliasedvar(EnvironmentCoordinate ec) {
}
AbortReasonOr<Ok> IonBuilder::jsop_in() {
MDefinition* obj = convertUnboxedObjects(current->pop());
MDefinition* obj = current->pop();
MDefinition* id = current->pop();
if (!forceInlineCaches()) {
@ -13562,7 +13498,7 @@ AbortReasonOr<Ok> IonBuilder::hasTryDefiniteSlotOrUnboxed(bool* emitted,
}
AbortReasonOr<Ok> IonBuilder::jsop_hasown() {
MDefinition* obj = convertUnboxedObjects(current->pop());
MDefinition* obj = current->pop();
MDefinition* id = current->pop();
if (!forceInlineCaches()) {

Просмотреть файл

@ -918,7 +918,6 @@ class IonBuilder : public MIRGenerator,
MDefinition* addShapeGuardsForGetterSetter(
MDefinition* obj, JSObject* holder, Shape* holderShape,
const BaselineInspector::ReceiverVector& receivers,
const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
bool isOwnProperty);
AbortReasonOr<Ok> annotateGetPropertyCache(MDefinition* obj,
@ -938,9 +937,7 @@ class IonBuilder : public MIRGenerator,
bool ownProperty = false);
uint32_t getDefiniteSlot(TemporaryTypeSet* types, jsid id, uint32_t* pnfixed);
MDefinition* convertUnboxedObjects(MDefinition* obj);
MDefinition* convertUnboxedObjects(
MDefinition* obj, const BaselineInspector::ObjectGroupVector& list);
uint32_t getUnboxedOffset(TemporaryTypeSet* types, jsid id,
JSValueType* punboxedType);
MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset,

Просмотреть файл

@ -911,7 +911,7 @@ IonBuilder::InliningResult IonBuilder::inlineArrayPopShift(
OBJECT_FLAG_SPARSE_INDEXES | OBJECT_FLAG_LENGTH_OVERFLOW |
OBJECT_FLAG_ITERATED | OBJECT_FLAG_NON_EXTENSIBLE_ELEMENTS;
MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
MDefinition* obj = callInfo.thisArg();
TemporaryTypeSet* thisTypes = obj->resultTypeSet();
if (!thisTypes) {
return InliningStatus_NotInlined;
@ -1018,7 +1018,7 @@ IonBuilder::InliningResult IonBuilder::inlineArrayPush(CallInfo& callInfo) {
return InliningStatus_NotInlined;
}
MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
MDefinition* obj = callInfo.thisArg();
for (uint32_t i = 0; i < callInfo.argc(); i++) {
MDefinition* value = callInfo.getArg(i);
if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj,
@ -1149,7 +1149,7 @@ IonBuilder::InliningResult IonBuilder::inlineArraySlice(CallInfo& callInfo) {
return InliningStatus_NotInlined;
}
MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
MDefinition* obj = callInfo.thisArg();
// Ensure |this| and result are objects.
if (getInlineReturnType() != MIRType::Object) {

Просмотреть файл

@ -84,9 +84,6 @@ bool SetImmutablePrototype(JSContext* cx, JS::HandleObject obj,
* as before.
* - JSObject::swap()
* - UnboxedPlainObject::convertToNative()
*
* NOTE: UnboxedObjects may change class without changing |group_|.
* - js::TryConvertToUnboxedLayout
*/
class JSObject : public js::gc::Cell {
protected:

Просмотреть файл

@ -195,6 +195,8 @@ class ObjectGroup : public gc::TenuredCell {
// assume objects of a previously seen group have the same class as before.
//
// See: TryConvertToUnboxedLayout
//
// MG:Unboxed: Verify above comment still holds
bool hasUncacheableClass() const { return clasp_->isNative(); }
bool hasDynamicPrototype() const { return proto_.isDynamic(); }

Просмотреть файл

@ -3855,8 +3855,6 @@ void PreliminaryObjectArrayWithTemplate::maybeAnalyze(JSContext* cx,
}
}
TryConvertToUnboxedLayout(cx, enter, shape(), group,
preliminaryObjects.get());
AutoSweepObjectGroup sweep(group);
if (group->maybeUnboxedLayout(sweep)) {
return;
@ -4156,12 +4154,6 @@ bool TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group,
initializerVector.length());
}
// Try to use an unboxed representation for the group.
if (!TryConvertToUnboxedLayout(cx, enter, templateObject()->lastProperty(),
group, preliminaryObjects)) {
return false;
}
js_delete(preliminaryObjects);
preliminaryObjects = nullptr;

Просмотреть файл

@ -1216,196 +1216,11 @@ const Class UnboxedPlainObject::class_ = {
// API
/////////////////////////////////////////////////////////////////////
static bool UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype) {
if (supertype == JSVAL_TYPE_DOUBLE && subtype == JSVAL_TYPE_INT32) {
return true;
}
if (supertype == JSVAL_TYPE_OBJECT && subtype == JSVAL_TYPE_NULL) {
return true;
}
return false;
}
static bool CombineUnboxedTypes(const Value& value, JSValueType* existing) {
JSValueType type =
value.isDouble() ? JSVAL_TYPE_DOUBLE : value.extractNonDoubleType();
if (*existing == JSVAL_TYPE_MAGIC || *existing == type ||
UnboxedTypeIncludes(type, *existing)) {
*existing = type;
return true;
}
if (UnboxedTypeIncludes(*existing, type)) {
return true;
}
return false;
}
// Return whether the property names and types in layout are a subset of the
// specified vector.
static bool PropertiesAreSuperset(
const UnboxedLayout::PropertyVector& properties, UnboxedLayout* layout) {
for (size_t i = 0; i < layout->properties().length(); i++) {
const UnboxedLayout::Property& layoutProperty = layout->properties()[i];
bool found = false;
for (size_t j = 0; j < properties.length(); j++) {
if (layoutProperty.name == properties[j].name) {
found = (layoutProperty.type == properties[j].type);
break;
}
}
if (!found) {
return false;
}
}
return true;
}
static bool CombinePlainObjectProperties(
PlainObject* obj, Shape* templateShape,
UnboxedLayout::PropertyVector& properties) {
// All preliminary objects must have been created with enough space to
// fill in their unboxed data inline. This is ensured either by using
// the largest allocation kind (which limits the maximum size of an
// unboxed object), or by using an allocation kind that covers all
// properties in the template, as the space used by unboxed properties
// is less than or equal to that used by boxed properties.
MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) >=
Min(NativeObject::MAX_FIXED_SLOTS, templateShape->slotSpan()));
if (obj->lastProperty() != templateShape || obj->hasDynamicElements()) {
// Only use an unboxed representation if all created objects match
// the template shape exactly.
return false;
}
for (size_t i = 0; i < templateShape->slotSpan(); i++) {
Value val = obj->getSlot(i);
JSValueType& existing = properties[i].type;
if (!CombineUnboxedTypes(val, &existing)) {
return false;
}
}
return true;
}
static size_t ComputePlainObjectLayout(
JSContext* cx, ObjectGroupRealm& realm, Shape* templateShape,
UnboxedLayout::PropertyVector& properties) {
// Fill in the names for all the object's properties.
for (Shape::Range<NoGC> r(templateShape); !r.empty(); r.popFront()) {
size_t slot = r.front().slot();
MOZ_ASSERT(!properties[slot].name);
properties[slot].name = JSID_TO_ATOM(r.front().propid())->asPropertyName();
}
// Fill in all the unboxed object's property offsets.
uint32_t offset = 0;
// Search for an existing unboxed layout which is a subset of this one.
// If there are multiple such layouts, use the largest one. If we're able
// to find such a layout, use the same property offsets for the shared
// properties, which will allow us to generate better code if the objects
// have a subtype/supertype relation and are accessed at common sites.
UnboxedLayout* bestExisting = nullptr;
for (UnboxedLayout* existing : realm.unboxedLayouts) {
if (PropertiesAreSuperset(properties, existing)) {
if (!bestExisting || existing->properties().length() >
bestExisting->properties().length()) {
bestExisting = existing;
}
}
}
if (bestExisting) {
for (size_t i = 0; i < bestExisting->properties().length(); i++) {
const UnboxedLayout::Property& existingProperty =
bestExisting->properties()[i];
for (size_t j = 0; j < templateShape->slotSpan(); j++) {
if (existingProperty.name == properties[j].name) {
MOZ_ASSERT(existingProperty.type == properties[j].type);
properties[j].offset = existingProperty.offset;
}
}
}
offset = bestExisting->size();
}
// Order remaining properties from the largest down for the best space
// utilization.
static const size_t typeSizes[] = {8, 4, 1};
for (size_t i = 0; i < ArrayLength(typeSizes); i++) {
size_t size = typeSizes[i];
for (size_t j = 0; j < templateShape->slotSpan(); j++) {
if (properties[j].offset != UINT32_MAX) {
continue;
}
JSValueType type = properties[j].type;
if (UnboxedTypeSize(type) == size) {
offset = JS_ROUNDUP(offset, size);
properties[j].offset = offset;
offset += size;
}
}
}
// The final offset is the amount of data needed by the object.
return offset;
}
static bool SetLayoutTraceList(JSContext* cx, UnboxedLayout* layout) {
// Figure out the offsets of any objects or string properties.
Vector<int32_t, 8, SystemAllocPolicy> objectOffsets, stringOffsets;
for (size_t i = 0; i < layout->properties().length(); i++) {
const UnboxedLayout::Property& property = layout->properties()[i];
MOZ_ASSERT(property.offset != UINT32_MAX);
if (property.type == JSVAL_TYPE_OBJECT) {
if (!objectOffsets.append(property.offset)) {
return false;
}
} else if (property.type == JSVAL_TYPE_STRING) {
if (!stringOffsets.append(property.offset)) {
return false;
}
}
}
// Construct the layout's trace list.
if (!objectOffsets.empty() || !stringOffsets.empty()) {
Vector<int32_t, 8, SystemAllocPolicy> entries;
if (!entries.appendAll(stringOffsets) || !entries.append(-1) ||
!entries.appendAll(objectOffsets) || !entries.append(-1) ||
!entries.append(-1)) {
return false;
}
int32_t* traceList = cx->zone()->pod_malloc<int32_t>(entries.length());
if (!traceList) {
return false;
}
PodCopy(traceList, entries.begin(), entries.length());
layout->setTraceList(traceList);
}
return true;
}
static inline Value NextValue(Handle<GCVector<Value>> values,
size_t* valueCursor) {
return values[(*valueCursor)++];
}
static bool GetValuesFromPreliminaryPlainObject(
PlainObject* obj, MutableHandle<GCVector<Value>> values) {
for (size_t i = 0; i < obj->slotSpan(); i++) {
if (!values.append(obj->getSlot(i))) {
return false;
}
}
return true;
}
void UnboxedPlainObject::fillAfterConvert(JSContext* cx,
Handle<GCVector<Value>> values,
size_t* valueCursor) {
@ -1415,159 +1230,4 @@ void UnboxedPlainObject::fillAfterConvert(JSContext* cx,
MOZ_ALWAYS_TRUE(
setValue(cx, layout().properties()[i], NextValue(values, valueCursor)));
}
}
bool js::TryConvertToUnboxedLayout(JSContext* cx, AutoEnterAnalysis& enter,
Shape* templateShape, ObjectGroup* group,
PreliminaryObjectArray* objects) {
MOZ_ASSERT(templateShape);
if (jit::JitOptions.disableUnboxedObjects) {
return true;
}
AutoSweepObjectGroup sweep(group);
MOZ_ASSERT(!templateShape->getObjectFlags());
if (group->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global())) {
return true;
}
if (templateShape->slotSpan() == 0) {
return true;
}
UnboxedLayout::PropertyVector properties;
if (!properties.appendN(UnboxedLayout::Property(),
templateShape->slotSpan())) {
return false;
}
size_t objectCount = 0;
for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
JSObject* obj = objects->get(i);
if (!obj) {
continue;
}
if (obj->isSingleton() || obj->group() != group) {
return true;
}
objectCount++;
if (!CombinePlainObjectProperties(&obj->as<PlainObject>(), templateShape,
properties)) {
return true;
}
}
size_t layoutSize = 0;
if (objectCount <= 1) {
// If only one of the objects has been created, it is more likely
// to have new properties added later.
return true;
}
for (size_t i = 0; i < templateShape->slotSpan(); i++) {
// We can't use an unboxed representation if e.g. all the objects have
// a null value for one of the properties, as we can't decide what type
// it is supposed to have.
if (UnboxedTypeSize(properties[i].type) == 0) {
return true;
}
}
// Make sure that all properties on the template shape are property
// names, and not indexes.
for (Shape::Range<NoGC> r(templateShape); !r.empty(); r.popFront()) {
jsid id = r.front().propid();
uint32_t dummy;
if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy)) {
return true;
}
}
ObjectGroupRealm& realm = ObjectGroupRealm::get(group);
layoutSize = ComputePlainObjectLayout(cx, realm, templateShape, properties);
// The entire object must be allocatable inline.
if (UnboxedPlainObject::offsetOfData() + layoutSize >
JSObject::MAX_BYTE_SIZE) {
return true;
}
UniquePtr<UnboxedLayout>& layout = enter.unboxedLayoutToCleanUp;
MOZ_ASSERT(!layout);
layout = group->zone()->make_unique<UnboxedLayout>(group->zone());
if (!layout) {
return false;
}
if (!layout->initProperties(properties, layoutSize)) {
return false;
}
// The unboxedLayouts list only tracks layouts for plain objects.
realm.unboxedLayouts.insertFront(layout.get());
if (!SetLayoutTraceList(cx, layout.get())) {
return false;
}
// We've determined that all the preliminary objects can use the new layout
// just constructed, so convert the existing group to use the unboxed class,
// and update the preliminary objects to use the new layout. Do the
// fallible stuff first before modifying any objects.
// Get an empty shape which we can use for the preliminary objects.
Shape* newShape = EmptyShape::getInitialShape(cx, &UnboxedPlainObject::class_,
group->proto(), 0);
if (!newShape) {
cx->recoverFromOutOfMemory();
return false;
}
// Accumulate a list of all the values in each preliminary object, and
// update their shapes.
Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
JSObject* obj = objects->get(i);
if (!obj) {
continue;
}
if (!GetValuesFromPreliminaryPlainObject(&obj->as<PlainObject>(),
&values)) {
cx->recoverFromOutOfMemory();
return false;
}
}
if (TypeNewScript* newScript = group->newScript(sweep)) {
layout->setNewScript(newScript);
}
for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
if (JSObject* obj = objects->get(i)) {
obj->as<NativeObject>().setLastPropertyMakeNonNative(newShape);
}
}
group->setClasp(&UnboxedPlainObject::class_);
group->setUnboxedLayout(layout.release());
size_t valueCursor = 0;
for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
JSObject* obj = objects->get(i);
if (!obj) {
continue;
}
obj->as<UnboxedPlainObject>().fillAfterConvert(cx, values, &valueCursor);
}
MOZ_ASSERT(valueCursor == values.length());
return true;
}
}

Просмотреть файл

@ -285,13 +285,6 @@ inline bool IsUnboxedObjectClass(const Class* class_) {
return class_ == &UnboxedPlainObject::class_;
}
// Try to construct an UnboxedLayout for each of the preliminary objects,
// provided they all match the template shape. If successful, converts the
// preliminary objects and their group to the new unboxed representation.
bool TryConvertToUnboxedLayout(JSContext* cx, AutoEnterAnalysis& enter,
Shape* templateShape, ObjectGroup* group,
PreliminaryObjectArray* objects);
} // namespace js
namespace JS {