From 1333cfd846c066e41db9b9c4b824bbf17290d5db Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Tue, 7 Jul 2015 11:20:25 -0700 Subject: [PATCH] Bug 1176751 - Eagerly convert unboxed arrays to native arrays more often during Ion compilation, r=jandem. --- js/src/jit/IonBuilder.cpp | 84 +++++++++++++++++++----------------- js/src/jit/IonBuilder.h | 6 +-- js/src/jit/MCallOptimize.cpp | 38 ++++++++-------- js/src/jit/MIR.cpp | 29 +++++++++++++ js/src/jit/MIR.h | 4 +- 5 files changed, 99 insertions(+), 62 deletions(-) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 195963fc4a8..7c1bdf9fb05 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -7724,6 +7724,8 @@ IonBuilder::jsop_getelem() } obj = maybeUnboxForPropertyAccess(obj); + if (obj->type() == MIRType_Object) + obj = convertUnboxedObjects(obj); bool emitted = false; @@ -8766,7 +8768,7 @@ IonBuilder::jsop_setelem() MDefinition* value = current->pop(); MDefinition* index = current->pop(); - MDefinition* object = current->pop(); + MDefinition* object = convertUnboxedObjects(current->pop()); trackTypeInfo(TrackedTypeSite::Receiver, object->type(), object->resultTypeSet()); trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet()); @@ -9478,8 +9480,7 @@ IonBuilder::jsop_rest() } uint32_t -IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed, - BaselineInspector::ObjectGroupVector& convertUnboxedGroups) +IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed) { if (!types || types->unknownObject()) { trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); @@ -9503,21 +9504,6 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_ return UINT32_MAX; } - // If we encounter a group for an unboxed property which has a - // corresponding native group, look for a definite slot in that native - // group, and force conversion of incoming objects to the native group. - if (key->isGroup() && key->group()->maybeUnboxedLayout()) { - if (ObjectGroup* nativeGroup = key->group()->unboxedLayout().nativeGroup()) { - if (!convertUnboxedGroups.append(key->group())) - CrashAtUnhandlableOOM("IonBuilder::getDefiniteSlot"); - key = TypeSet::ObjectKey::get(nativeGroup); - if (key->unknownProperties()) { - trackOptimizationOutcome(TrackedOutcome::UnknownProperties); - return UINT32_MAX; - } - } - } - HeapTypeSetKey property = key->property(NameToId(name)); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || @@ -10033,6 +10019,8 @@ IonBuilder::jsop_getprop(PropertyName* name) } obj = maybeUnboxForPropertyAccess(obj); + if (obj->type() == MIRType_Object) + obj = convertUnboxedObjects(obj); BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj, name, types); @@ -10513,12 +10501,43 @@ 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()) + 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()) { + if (layout->nativeGroup() && !list.append(key->group())) + CrashAtUnhandlableOOM("IonBuilder::convertUnboxedObjects"); + } + } + + return convertUnboxedObjects(obj, list); +} + MDefinition* IonBuilder::convertUnboxedObjects(MDefinition* obj, const BaselineInspector::ObjectGroupVector& list) { for (size_t i = 0; i < list.length(); i++) { - obj = MConvertUnboxedObjectToNative::New(alloc(), obj, list[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; @@ -10530,10 +10549,8 @@ IonBuilder::getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName { MOZ_ASSERT(*emitted == false); - BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); - uint32_t nfixed; - uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), name, &nfixed, convertUnboxedGroups); + uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), name, &nfixed); if (slot == UINT32_MAX) return true; @@ -10543,8 +10560,6 @@ IonBuilder::getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName obj = guard; } - obj = convertUnboxedObjects(obj, convertUnboxedGroups); - MInstruction* load; if (slot < nfixed) { load = MLoadFixedSlot::New(alloc(), obj, slot); @@ -11151,7 +11166,7 @@ bool IonBuilder::jsop_setprop(PropertyName* name) { MDefinition* value = current->pop(); - MDefinition* obj = current->pop(); + MDefinition* obj = convertUnboxedObjects(current->pop()); bool emitted = false; startTrackingOptimizations(); @@ -11450,10 +11465,8 @@ IonBuilder::setPropTryDefiniteSlot(bool* emitted, MDefinition* obj, return true; } - BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); - uint32_t nfixed; - uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), name, &nfixed, convertUnboxedGroups); + uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), name, &nfixed); if (slot == UINT32_MAX) return true; @@ -11471,8 +11484,6 @@ IonBuilder::setPropTryDefiniteSlot(bool* emitted, MDefinition* obj, writeBarrier |= property.needsBarrier(constraints()); } - obj = convertUnboxedObjects(obj, convertUnboxedGroups); - MInstruction* store; if (slot < nfixed) { store = MStoreFixedSlot::New(alloc(), obj, slot, value); @@ -12308,8 +12319,8 @@ IonBuilder::jsop_setaliasedvar(ScopeCoordinate sc) bool IonBuilder::jsop_in() { - MDefinition* obj = current->peek(-1); - MDefinition* id = current->peek(-2); + MDefinition* obj = convertUnboxedObjects(current->pop()); + MDefinition* id = current->pop(); do { if (shouldAbortOnPreliminaryGroups(obj)) @@ -12324,11 +12335,9 @@ IonBuilder::jsop_in() if (ElementAccessHasExtraIndexedProperty(constraints(), obj)) break; - return jsop_in_dense(unboxedType); + return jsop_in_dense(obj, id, unboxedType); } while (false); - current->pop(); - current->pop(); MIn* ins = MIn::New(alloc(), id, obj); current->add(ins); @@ -12338,11 +12347,8 @@ IonBuilder::jsop_in() } bool -IonBuilder::jsop_in_dense(JSValueType unboxedType) +IonBuilder::jsop_in_dense(MDefinition* obj, MDefinition* id, JSValueType unboxedType) { - MDefinition* obj = current->pop(); - MDefinition* id = current->pop(); - bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj); // Ensure id is an integer. diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 90ef34d3060..4bba6bddc73 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -692,7 +692,7 @@ class IonBuilder bool jsop_isnoiter(); bool jsop_iterend(); bool jsop_in(); - bool jsop_in_dense(JSValueType unboxedType); + bool jsop_in_dense(MDefinition* obj, MDefinition* id, JSValueType unboxedType); bool jsop_instanceof(); bool jsop_getaliasedvar(ScopeCoordinate sc); bool jsop_setaliasedvar(ScopeCoordinate sc); @@ -927,8 +927,8 @@ class IonBuilder JSObject* testSingletonProperty(JSObject* obj, PropertyName* name); JSObject* testSingletonPropertyTypes(MDefinition* obj, PropertyName* name); - uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed, - BaselineInspector::ObjectGroupVector& convertUnboxedGroups); + 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, diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 81a7fb94831..2bc79feb7ae 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -632,7 +632,7 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode) OBJECT_FLAG_LENGTH_OVERFLOW | OBJECT_FLAG_ITERATED; - MDefinition* obj = callInfo.thisArg(); + MDefinition* obj = convertUnboxedObjects(callInfo.thisArg()); TemporaryTypeSet* thisTypes = obj->resultTypeSet(); if (!thisTypes) return InliningStatus_NotInlined; @@ -758,7 +758,7 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo) return InliningStatus_NotInlined; } - MDefinition* obj = callInfo.thisArg(); + MDefinition* obj = convertUnboxedObjects(callInfo.thisArg()); MDefinition* value = callInfo.getArg(0); if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, nullptr, &value, /* canModify = */ false)) @@ -839,17 +839,20 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo) return InliningStatus_NotInlined; } + MDefinition* thisArg = convertUnboxedObjects(callInfo.thisArg()); + MDefinition* objArg = convertUnboxedObjects(callInfo.getArg(0)); + // Ensure |this|, argument and result are objects. if (getInlineReturnType() != MIRType_Object) return InliningStatus_NotInlined; - if (callInfo.thisArg()->type() != MIRType_Object) + if (thisArg->type() != MIRType_Object) return InliningStatus_NotInlined; - if (callInfo.getArg(0)->type() != MIRType_Object) + if (objArg->type() != MIRType_Object) return InliningStatus_NotInlined; // |this| and the argument must be dense arrays. - TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet(); - TemporaryTypeSet* argTypes = callInfo.getArg(0)->resultTypeSet(); + TemporaryTypeSet* thisTypes = thisArg->resultTypeSet(); + TemporaryTypeSet* argTypes = objArg->resultTypeSet(); if (!thisTypes || !argTypes) return InliningStatus_NotInlined; @@ -874,10 +877,10 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo) JSValueType unboxedType = JSVAL_TYPE_MAGIC; if (clasp == &UnboxedArrayObject::class_) { - unboxedType = UnboxedArrayElementType(constraints(), callInfo.thisArg(), nullptr); + unboxedType = UnboxedArrayElementType(constraints(), thisArg, nullptr); if (unboxedType == JSVAL_TYPE_MAGIC) return InliningStatus_NotInlined; - if (unboxedType != UnboxedArrayElementType(constraints(), callInfo.getArg(0), nullptr)) + if (unboxedType != UnboxedArrayElementType(constraints(), objArg, nullptr)) return InliningStatus_NotInlined; } @@ -937,8 +940,7 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo) callInfo.setImplicitlyUsedUnchecked(); - MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), - callInfo.thisArg(), callInfo.getArg(0), + MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), thisArg, objArg, templateObj, templateObj->group()->initialHeap(constraints()), unboxedType); @@ -958,10 +960,12 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) return InliningStatus_NotInlined; } + MDefinition* obj = convertUnboxedObjects(callInfo.thisArg()); + // Ensure |this| and result are objects. if (getInlineReturnType() != MIRType_Object) return InliningStatus_NotInlined; - if (callInfo.thisArg()->type() != MIRType_Object) + if (obj->type() != MIRType_Object) return InliningStatus_NotInlined; // Arguments for the sliced region must be integers. @@ -975,7 +979,7 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) } // |this| must be a dense array. - TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet(); + TemporaryTypeSet* thisTypes = obj->resultTypeSet(); if (!thisTypes) return InliningStatus_NotInlined; @@ -991,7 +995,7 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) JSValueType unboxedType = JSVAL_TYPE_MAGIC; if (clasp == &UnboxedArrayObject::class_) { - unboxedType = UnboxedArrayElementType(constraints(), callInfo.thisArg(), nullptr); + unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr); if (unboxedType == JSVAL_TYPE_MAGIC) return InliningStatus_NotInlined; } @@ -1038,18 +1042,18 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) if (callInfo.argc() > 1) { end = callInfo.getArg(1); } else if (clasp == &ArrayObject::class_) { - MElements* elements = MElements::New(alloc(), callInfo.thisArg()); + MElements* elements = MElements::New(alloc(), obj); current->add(elements); end = MArrayLength::New(alloc(), elements); current->add(end->toInstruction()); } else { - end = MUnboxedArrayLength::New(alloc(), callInfo.thisArg()); + end = MUnboxedArrayLength::New(alloc(), obj); current->add(end->toInstruction()); } MArraySlice* ins = MArraySlice::New(alloc(), constraints(), - callInfo.thisArg(), begin, end, + obj, begin, end, templateObj, templateObj->group()->initialHeap(constraints()), unboxedType); @@ -2114,7 +2118,7 @@ IonBuilder::inlineDefineDataProperty(CallInfo& callInfo) if (callInfo.argc() != 3) return InliningStatus_NotInlined; - MDefinition* obj = callInfo.getArg(0); + MDefinition* obj = convertUnboxedObjects(callInfo.getArg(0)); MDefinition* id = callInfo.getArg(1); MDefinition* value = callInfo.getArg(2); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index fb27e95f99a..6d69e15af32 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -4757,6 +4757,35 @@ MArrayJoin::foldsTo(TempAllocator& alloc) return MStringReplace::New(alloc, string, pattern, replacement); } +MConvertUnboxedObjectToNative* +MConvertUnboxedObjectToNative::New(TempAllocator& alloc, MDefinition* obj, ObjectGroup* group) +{ + MConvertUnboxedObjectToNative* res = new(alloc) MConvertUnboxedObjectToNative(obj, group); + + ObjectGroup* nativeGroup = group->unboxedLayout().nativeGroup(); + + // Make a new type set for the result of this instruction which replaces + // the input group with the native group we will convert it to. + TemporaryTypeSet* types = obj->resultTypeSet(); + if (types && !types->unknownObject()) { + TemporaryTypeSet* newTypes = types->cloneWithoutObjects(alloc.lifoAlloc()); + if (newTypes) { + for (size_t i = 0; i < types->getObjectCount(); i++) { + TypeSet::ObjectKey* key = types->getObject(i); + if (!key) + continue; + if (key->unknownProperties() || !key->isGroup() || key->group() != group) + newTypes->addType(TypeSet::ObjectType(key), alloc.lifoAlloc()); + else + newTypes->addType(TypeSet::ObjectType(nativeGroup), alloc.lifoAlloc()); + } + res->setResultTypeSet(newTypes); + } + } + + return res; +} + bool jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index a60e00997ec..ca5ecf616e1 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9049,9 +9049,7 @@ class MConvertUnboxedObjectToNative INSTRUCTION_HEADER(ConvertUnboxedObjectToNative) static MConvertUnboxedObjectToNative* New(TempAllocator& alloc, MDefinition* obj, - ObjectGroup* group) { - return new(alloc) MConvertUnboxedObjectToNative(obj, group); - } + ObjectGroup* group); MDefinition* object() const { return getOperand(0);