mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 08:18:41 +09:00
remove unboxed code chunk (wip1)
This commit is contained in:
parent
3c6346afa9
commit
7b0eb7be8c
6 changed files with 25 additions and 507 deletions
|
|
@ -96,13 +96,8 @@ VectorAppendNoDuplicate(S& list, T value)
|
|||
|
||||
static bool
|
||||
AddReceiver(const ReceiverGuard& receiver,
|
||||
BaselineInspector::ReceiverVector& receivers,
|
||||
BaselineInspector::ObjectGroupVector& convertUnboxedGroups)
|
||||
BaselineInspector::ReceiverVector& receivers)
|
||||
{
|
||||
if (receiver.group && receiver.group->maybeUnboxedLayout()) {
|
||||
if (receiver.group->unboxedLayout().nativeGroup())
|
||||
return VectorAppendNoDuplicate(convertUnboxedGroups, receiver.group);
|
||||
}
|
||||
return VectorAppendNoDuplicate(receivers, receiver);
|
||||
}
|
||||
|
||||
|
|
@ -170,16 +165,12 @@ GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Monitored* stub, ReceiverGuard* r
|
|||
}
|
||||
|
||||
bool
|
||||
BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers,
|
||||
ObjectGroupVector& convertUnboxedGroups)
|
||||
BaselineInspector::maybeInfoForPropertyOp(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 (!hasBaselineScript())
|
||||
return true;
|
||||
|
|
@ -207,7 +198,7 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receiv
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!AddReceiver(receiver, receivers, convertUnboxedGroups))
|
||||
if (!AddReceiver(receiver, receivers))
|
||||
return false;
|
||||
|
||||
stub = stub->next();
|
||||
|
|
@ -700,14 +691,12 @@ bool
|
|||
BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
|
||||
JSFunction** commonGetter, Shape** globalShape,
|
||||
bool* isOwnProperty,
|
||||
ReceiverVector& receivers,
|
||||
ObjectGroupVector& convertUnboxedGroups)
|
||||
ReceiverVector& receivers)
|
||||
{
|
||||
if (!hasBaselineScript())
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(receivers.empty());
|
||||
MOZ_ASSERT(convertUnboxedGroups.empty());
|
||||
|
||||
*holder = nullptr;
|
||||
const ICEntry& entry = icEntryFromPC(pc);
|
||||
|
|
@ -719,7 +708,7 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shap
|
|||
{
|
||||
ICGetPropCallGetter* nstub = static_cast<ICGetPropCallGetter*>(stub);
|
||||
bool isOwn = nstub->isOwnGetter();
|
||||
if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups))
|
||||
if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers))
|
||||
return false;
|
||||
|
||||
if (!*holder) {
|
||||
|
|
@ -751,21 +740,19 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shap
|
|||
if (!*holder)
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(*isOwnProperty == (receivers.empty() && convertUnboxedGroups.empty()));
|
||||
MOZ_ASSERT(*isOwnProperty == (receivers.empty()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
|
||||
JSFunction** commonSetter, bool* isOwnProperty,
|
||||
ReceiverVector& receivers,
|
||||
ObjectGroupVector& convertUnboxedGroups)
|
||||
ReceiverVector& receivers)
|
||||
{
|
||||
if (!hasBaselineScript())
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(receivers.empty());
|
||||
MOZ_ASSERT(convertUnboxedGroups.empty());
|
||||
|
||||
*holder = nullptr;
|
||||
const ICEntry& entry = icEntryFromPC(pc);
|
||||
|
|
@ -774,7 +761,7 @@ BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shap
|
|||
if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
|
||||
ICSetPropCallSetter* nstub = static_cast<ICSetPropCallSetter*>(stub);
|
||||
bool isOwn = nstub->isOwnSetter();
|
||||
if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups))
|
||||
if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers))
|
||||
return false;
|
||||
|
||||
if (!*holder) {
|
||||
|
|
|
|||
|
|
@ -95,8 +95,7 @@ class BaselineInspector
|
|||
public:
|
||||
typedef Vector<ReceiverGuard, 4, JitAllocPolicy> ReceiverVector;
|
||||
typedef Vector<ObjectGroup*, 4, JitAllocPolicy> ObjectGroupVector;
|
||||
MOZ_MUST_USE bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers,
|
||||
ObjectGroupVector& convertUnboxedGroups);
|
||||
MOZ_MUST_USE bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers);
|
||||
|
||||
SetElemICInspector setElemICInspector(jsbytecode* pc) {
|
||||
return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback);
|
||||
|
|
@ -131,12 +130,10 @@ class BaselineInspector
|
|||
|
||||
MOZ_MUST_USE bool commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
|
||||
JSFunction** commonGetter, Shape** globalShape,
|
||||
bool* isOwnProperty, ReceiverVector& receivers,
|
||||
ObjectGroupVector& convertUnboxedGroups);
|
||||
bool* isOwnProperty, ReceiverVector& receivers);
|
||||
MOZ_MUST_USE bool commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
|
||||
JSFunction** commonSetter, bool* isOwnProperty,
|
||||
ReceiverVector& receivers,
|
||||
ObjectGroupVector& convertUnboxedGroups);
|
||||
ReceiverVector& receivers);
|
||||
|
||||
MOZ_MUST_USE bool instanceOfData(jsbytecode* pc, Shape** shape, uint32_t* slot,
|
||||
JSObject** prototypeObject);
|
||||
|
|
|
|||
|
|
@ -9012,8 +9012,6 @@ IonBuilder::jsop_getelem()
|
|||
}
|
||||
|
||||
obj = maybeUnboxForPropertyAccess(obj);
|
||||
if (obj->type() == MIRType::Object)
|
||||
obj = convertUnboxedObjects(obj);
|
||||
|
||||
bool emitted = false;
|
||||
|
||||
|
|
@ -10137,7 +10135,7 @@ 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());
|
||||
trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet());
|
||||
|
|
@ -11496,8 +11494,6 @@ IonBuilder::jsop_getprop(PropertyName* name)
|
|||
}
|
||||
|
||||
obj = maybeUnboxForPropertyAccess(obj);
|
||||
if (obj->type() == MIRType::Object)
|
||||
obj = convertUnboxedObjects(obj);
|
||||
|
||||
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
|
||||
obj, name, types);
|
||||
|
|
@ -11939,49 +11935,6 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool* emitted,
|
|||
fieldPrediction, 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;
|
||||
|
||||
if (UnboxedLayout* layout = key->group()->maybeUnboxedLayout()) {
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name,
|
||||
BarrierKind barrier, TemporaryTypeSet* types)
|
||||
|
|
@ -12163,14 +12116,11 @@ IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* nam
|
|||
MDefinition*
|
||||
IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
|
||||
const BaselineInspector::ReceiverVector& receivers,
|
||||
const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
|
||||
bool isOwnProperty)
|
||||
{
|
||||
MOZ_ASSERT(holder);
|
||||
MOZ_ASSERT(holderShape);
|
||||
|
||||
obj = convertUnboxedObjects(obj, convertUnboxedGroups);
|
||||
|
||||
if (isOwnProperty) {
|
||||
MOZ_ASSERT(receivers.empty());
|
||||
return addShapeGuard(obj, holderShape, Bailout_ShapeGuard);
|
||||
|
|
@ -12194,10 +12144,8 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName
|
|||
JSObject* foundProto = nullptr;
|
||||
bool isOwnProperty = false;
|
||||
BaselineInspector::ReceiverVector receivers(alloc());
|
||||
BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
|
||||
if (!inspector->commonGetPropFunction(pc, &foundProto, &lastProperty, &commonGetter,
|
||||
&globalShape, &isOwnProperty,
|
||||
receivers, convertUnboxedGroups))
|
||||
&globalShape, &isOwnProperty, receivers))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -12213,8 +12161,7 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName
|
|||
// If type information is bad, we can still optimize the getter if we
|
||||
// shape guard.
|
||||
obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
|
||||
receivers, convertUnboxedGroups,
|
||||
isOwnProperty);
|
||||
receivers, isOwnProperty);
|
||||
if (!obj)
|
||||
return false;
|
||||
}
|
||||
|
|
@ -12381,15 +12328,12 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName
|
|||
MOZ_ASSERT(*emitted == false);
|
||||
|
||||
BaselineInspector::ReceiverVector receivers(alloc());
|
||||
BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
|
||||
if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
|
||||
if (!inspector->maybeInfoForPropertyOp(pc, receivers))
|
||||
return false;
|
||||
|
||||
if (!canInlinePropertyOpShapes(receivers))
|
||||
return true;
|
||||
|
||||
obj = convertUnboxedObjects(obj, convertUnboxedGroups);
|
||||
|
||||
MIRType rvalType = types->getKnownMIRType();
|
||||
if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
|
||||
rvalType = MIRType::Value;
|
||||
|
|
@ -12692,7 +12636,7 @@ bool
|
|||
IonBuilder::jsop_setprop(PropertyName* name)
|
||||
{
|
||||
MDefinition* value = current->pop();
|
||||
MDefinition* obj = convertUnboxedObjects(current->pop());
|
||||
MDefinition* obj = current->pop();
|
||||
|
||||
bool emitted = false;
|
||||
startTrackingOptimizations();
|
||||
|
|
@ -12765,10 +12709,8 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj,
|
|||
JSObject* foundProto = nullptr;
|
||||
bool isOwnProperty;
|
||||
BaselineInspector::ReceiverVector receivers(alloc());
|
||||
BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
|
||||
if (!inspector->commonSetPropFunction(pc, &foundProto, &lastProperty, &commonSetter,
|
||||
&isOwnProperty,
|
||||
receivers, convertUnboxedGroups))
|
||||
&isOwnProperty, receivers))
|
||||
{
|
||||
trackOptimizationOutcome(TrackedOutcome::NoProtoFound);
|
||||
return true;
|
||||
|
|
@ -12783,8 +12725,7 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj,
|
|||
// If 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 false;
|
||||
}
|
||||
|
|
@ -13146,15 +13087,12 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj,
|
|||
}
|
||||
|
||||
BaselineInspector::ReceiverVector receivers(alloc());
|
||||
BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
|
||||
if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
|
||||
if (!inspector->maybeInfoForPropertyOp(pc, receivers))
|
||||
return false;
|
||||
|
||||
if (!canInlinePropertyOpShapes(receivers))
|
||||
return true;
|
||||
|
||||
obj = convertUnboxedObjects(obj, convertUnboxedGroups);
|
||||
|
||||
if (receivers.length() == 1) {
|
||||
if (!receivers[0].group) {
|
||||
// Monomorphic store to a native object.
|
||||
|
|
@ -13884,7 +13822,7 @@ IonBuilder::jsop_setaliasedvar(EnvironmentCoordinate ec)
|
|||
bool
|
||||
IonBuilder::jsop_in()
|
||||
{
|
||||
MDefinition* obj = convertUnboxedObjects(current->pop());
|
||||
MDefinition* obj = current->pop();
|
||||
MDefinition* id = current->pop();
|
||||
|
||||
bool emitted = false;
|
||||
|
|
|
|||
|
|
@ -1041,7 +1041,6 @@ class IonBuilder
|
|||
MDefinition*
|
||||
addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
|
||||
const BaselineInspector::ReceiverVector& receivers,
|
||||
const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
|
||||
bool isOwnProperty);
|
||||
|
||||
MOZ_MUST_USE bool annotateGetPropertyCache(MDefinition* obj, PropertyName* name,
|
||||
|
|
@ -1059,9 +1058,6 @@ class IonBuilder
|
|||
ResultWithOOM<bool> testNotDefinedProperty(MDefinition* obj, jsid id);
|
||||
|
||||
uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed);
|
||||
MDefinition* convertUnboxedObjects(MDefinition* obj);
|
||||
MDefinition* convertUnboxedObjects(MDefinition* obj,
|
||||
const BaselineInspector::ObjectGroupVector& list);
|
||||
uint32_t getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name,
|
||||
JSValueType* punboxedType);
|
||||
MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
|
||||
|
|
|
|||
|
|
@ -615,7 +615,7 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
|
|||
OBJECT_FLAG_LENGTH_OVERFLOW |
|
||||
OBJECT_FLAG_ITERATED;
|
||||
|
||||
MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
|
||||
MDefinition* obj = callInfo.thisArg();
|
||||
TemporaryTypeSet* thisTypes = obj->resultTypeSet();
|
||||
if (!thisTypes)
|
||||
return InliningStatus_NotInlined;
|
||||
|
|
@ -743,7 +743,7 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
|
|||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
|
||||
MDefinition* obj = callInfo.thisArg();
|
||||
MDefinition* value = callInfo.getArg(0);
|
||||
if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
|
||||
&obj, nullptr, &value, /* canModify = */ false))
|
||||
|
|
@ -822,7 +822,7 @@ 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)
|
||||
|
|
@ -2152,7 +2152,7 @@ IonBuilder::inlineDefineDataProperty(CallInfo& callInfo)
|
|||
if (callInfo.argc() != 3)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MDefinition* obj = convertUnboxedObjects(callInfo.getArg(0));
|
||||
MDefinition* obj = callInfo.getArg(0);
|
||||
MDefinition* id = callInfo.getArg(1);
|
||||
MDefinition* value = callInfo.getArg(2);
|
||||
|
||||
|
|
|
|||
|
|
@ -1655,227 +1655,12 @@ const Class UnboxedArrayObject::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 bool
|
||||
CombineArrayObjectElements(ExclusiveContext* cx, ArrayObject* obj, JSValueType* elementType)
|
||||
{
|
||||
if (obj->inDictionaryMode() ||
|
||||
obj->lastProperty()->propid() != AtomToId(cx->names().length) ||
|
||||
!obj->lastProperty()->previous()->isEmptyShape())
|
||||
{
|
||||
// Only use an unboxed representation if the object has no properties.
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
|
||||
Value val = obj->getDenseElement(i);
|
||||
|
||||
// For now, unboxed arrays cannot have holes.
|
||||
if (val.isMagic(JS_ELEMENTS_HOLE))
|
||||
return false;
|
||||
|
||||
if (!CombineUnboxedTypes(val, elementType))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t
|
||||
ComputePlainObjectLayout(ExclusiveContext* cx, 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 : cx->compartment()->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(ExclusiveContext* 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
|
||||
GetValuesFromPreliminaryArrayObject(ArrayObject* obj, MutableHandle<GCVector<Value>> values)
|
||||
{
|
||||
if (!values.append(Int32Value(obj->length())))
|
||||
return false;
|
||||
if (!values.append(Int32Value(obj->getDenseInitializedLength())))
|
||||
return false;
|
||||
for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
|
||||
if (!values.append(obj->getDenseElement(i)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx,
|
||||
Handle<GCVector<Value>> values, size_t* valueCursor)
|
||||
|
|
@ -1901,16 +1686,6 @@ UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx,
|
|||
JS_ALWAYS_TRUE(initElement(cx, i, NextValue(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(ExclusiveContext* cx,
|
||||
Handle<GCVector<Value>> values, size_t* valueCursor)
|
||||
|
|
@ -1921,181 +1696,6 @@ UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx,
|
|||
JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor)));
|
||||
}
|
||||
|
||||
bool
|
||||
js::TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Shape* templateShape,
|
||||
ObjectGroup* group, PreliminaryObjectArray* objects)
|
||||
{
|
||||
bool isArray = !templateShape;
|
||||
|
||||
// Unboxed arrays are nightly only for now. The getenv() call will be
|
||||
// removed when they are on by default. See bug 1153266.
|
||||
if (isArray) {
|
||||
#ifdef NIGHTLY_BUILD
|
||||
if (!getenv("JS_OPTION_USE_UNBOXED_ARRAYS")) {
|
||||
if (!cx->options().unboxedArrays())
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
} else {
|
||||
if (jit::JitOptions.disableUnboxedObjects)
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(templateShape, !templateShape->getObjectFlags());
|
||||
|
||||
if (group->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global()))
|
||||
return true;
|
||||
|
||||
if (!isArray && templateShape->slotSpan() == 0)
|
||||
return true;
|
||||
|
||||
UnboxedLayout::PropertyVector properties;
|
||||
if (!isArray) {
|
||||
if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan()))
|
||||
return false;
|
||||
}
|
||||
JSValueType elementType = JSVAL_TYPE_MAGIC;
|
||||
|
||||
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 (isArray) {
|
||||
if (!CombineArrayObjectElements(cx, &obj->as<ArrayObject>(), &elementType))
|
||||
return true;
|
||||
} else {
|
||||
if (!CombinePlainObjectProperties(&obj->as<PlainObject>(), templateShape, properties))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
size_t layoutSize = 0;
|
||||
if (isArray) {
|
||||
// Don't use an unboxed representation if we couldn't determine an
|
||||
// element type for the objects.
|
||||
if (UnboxedTypeSize(elementType) == 0)
|
||||
return true;
|
||||
} else {
|
||||
if (objectCount <= 1) {
|
||||
// If only one of the objects has been created, it is more likely
|
||||
// to have new properties added later. This heuristic is not used
|
||||
// for array objects, where we might want an unboxed representation
|
||||
// even if there is only one large array.
|
||||
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;
|
||||
}
|
||||
|
||||
layoutSize = ComputePlainObjectLayout(cx, 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>();
|
||||
if (!layout)
|
||||
return false;
|
||||
|
||||
if (isArray) {
|
||||
layout->initArray(elementType);
|
||||
} else {
|
||||
if (!layout->initProperties(properties, layoutSize))
|
||||
return false;
|
||||
|
||||
// The unboxedLayouts list only tracks layouts for plain objects.
|
||||
cx->compartment()->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.
|
||||
const Class* clasp = isArray ? &UnboxedArrayObject::class_ : &UnboxedPlainObject::class_;
|
||||
Shape* newShape = EmptyShape::getInitialShape(cx, clasp, 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;
|
||||
|
||||
bool ok;
|
||||
if (isArray)
|
||||
ok = GetValuesFromPreliminaryArrayObject(&obj->as<ArrayObject>(), &values);
|
||||
else
|
||||
ok = GetValuesFromPreliminaryPlainObject(&obj->as<PlainObject>(), &values);
|
||||
|
||||
if (!ok) {
|
||||
cx->recoverFromOutOfMemory();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (TypeNewScript* newScript = group->newScript())
|
||||
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(clasp);
|
||||
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;
|
||||
|
||||
if (isArray)
|
||||
obj->as<UnboxedArrayObject>().fillAfterConvert(cx, values, &valueCursor);
|
||||
else
|
||||
obj->as<UnboxedPlainObject>().fillAfterConvert(cx, values, &valueCursor);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(valueCursor == values.length());
|
||||
return true;
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements,
|
||||
ExclusiveContext*, JSObject*, uint32_t, const Value*, uint32_t,
|
||||
ShouldUpdateTypes);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue