From c22a493144e39d76bfa42c46f9d6d17a5143ac35 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Sat, 22 Feb 2020 21:09:32 +0100 Subject: Revert #1142 - Remove unboxed objects - accounting for removal of watch()/unwatch() --- js/src/jit/IonBuilder.cpp | 183 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) (limited to 'js/src/jit/IonBuilder.cpp') diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 1e12f5dbe..f00167d92 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -10940,6 +10940,63 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_ return slot; } +uint32_t +IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name, JSValueType* punboxedType) +{ + if (!types || types->unknownObject() || !types->objectOrSentinel()) { + trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); + return UINT32_MAX; + } + + uint32_t offset = UINT32_MAX; + + for (size_t i = 0; i < types->getObjectCount(); i++) { + TypeSet::ObjectKey* key = types->getObject(i); + if (!key) + continue; + + if (key->unknownProperties()) { + trackOptimizationOutcome(TrackedOutcome::UnknownProperties); + return UINT32_MAX; + } + + if (key->isSingleton()) { + trackOptimizationOutcome(TrackedOutcome::Singleton); + return UINT32_MAX; + } + + UnboxedLayout* layout = key->group()->maybeUnboxedLayout(); + if (!layout) { + trackOptimizationOutcome(TrackedOutcome::NotUnboxed); + return UINT32_MAX; + } + + const UnboxedLayout::Property* property = layout->lookup(name); + if (!property) { + trackOptimizationOutcome(TrackedOutcome::StructNoField); + return UINT32_MAX; + } + + if (layout->nativeGroup()) { + trackOptimizationOutcome(TrackedOutcome::UnboxedConvertedToNative); + return UINT32_MAX; + } + + if (offset == UINT32_MAX) { + offset = property->offset; + *punboxedType = property->type; + } else if (offset != property->offset) { + trackOptimizationOutcome(TrackedOutcome::InconsistentFieldOffset); + return UINT32_MAX; + } else if (*punboxedType != property->type) { + trackOptimizationOutcome(TrackedOutcome::InconsistentFieldType); + return UINT32_MAX; + } + } + + return offset; +} + bool IonBuilder::jsop_runonce() { @@ -11906,6 +11963,72 @@ IonBuilder::getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyN return true; } +MInstruction* +IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, + BarrierKind barrier, TemporaryTypeSet* types) +{ + // loadUnboxedValue is designed to load any value as if it were contained in + // an array. Thus a property offset is converted to an index, when the + // object is reinterpreted as an array of properties of the same size. + size_t index = offset / UnboxedTypeSize(unboxedType); + MInstruction* indexConstant = MConstant::New(alloc(), Int32Value(index)); + current->add(indexConstant); + + return loadUnboxedValue(obj, UnboxedPlainObject::offsetOfData(), + indexConstant, unboxedType, barrier, types); +} + +MInstruction* +IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset, + MDefinition* index, JSValueType unboxedType, + BarrierKind barrier, TemporaryTypeSet* types) +{ + MInstruction* load; + switch (unboxedType) { + case JSVAL_TYPE_BOOLEAN: + load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Uint8, + DoesNotRequireMemoryBarrier, elementsOffset); + load->setResultType(MIRType::Boolean); + break; + + case JSVAL_TYPE_INT32: + load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Int32, + DoesNotRequireMemoryBarrier, elementsOffset); + load->setResultType(MIRType::Int32); + break; + + case JSVAL_TYPE_DOUBLE: + load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Float64, + DoesNotRequireMemoryBarrier, elementsOffset, + /* canonicalizeDoubles = */ false); + load->setResultType(MIRType::Double); + break; + + case JSVAL_TYPE_STRING: + load = MLoadUnboxedString::New(alloc(), elements, index, elementsOffset); + break; + + case JSVAL_TYPE_OBJECT: { + MLoadUnboxedObjectOrNull::NullBehavior nullBehavior; + if (types->hasType(TypeSet::NullType())) + nullBehavior = MLoadUnboxedObjectOrNull::HandleNull; + else if (barrier != BarrierKind::NoBarrier) + nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull; + else + nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible; + load = MLoadUnboxedObjectOrNull::New(alloc(), elements, index, nullBehavior, + elementsOffset); + break; + } + + default: + MOZ_CRASH(); + } + + current->add(load); + return load; +} + MDefinition* IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape, const BaselineInspector::ReceiverVector& receivers, @@ -12729,6 +12852,66 @@ IonBuilder::setPropTryDefiniteSlot(bool* emitted, MDefinition* obj, return true; } +MInstruction* +IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, + MDefinition* value) +{ + size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType); + MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant)); + current->add(scaledOffset); + + return storeUnboxedValue(obj, obj, UnboxedPlainObject::offsetOfData(), + scaledOffset, unboxedType, value); +} + +MInstruction* +IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset, + MDefinition* scaledOffset, JSValueType unboxedType, + MDefinition* value, bool preBarrier /* = true */) +{ + MInstruction* store; + switch (unboxedType) { + case JSVAL_TYPE_BOOLEAN: + store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8, + MStoreUnboxedScalar::DontTruncateInput, + DoesNotRequireMemoryBarrier, elementsOffset); + break; + + case JSVAL_TYPE_INT32: + store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32, + MStoreUnboxedScalar::DontTruncateInput, + DoesNotRequireMemoryBarrier, elementsOffset); + break; + + case JSVAL_TYPE_DOUBLE: + store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64, + MStoreUnboxedScalar::DontTruncateInput, + DoesNotRequireMemoryBarrier, elementsOffset); + break; + + case JSVAL_TYPE_STRING: + store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, + elementsOffset, preBarrier); + break; + + case JSVAL_TYPE_OBJECT: + MOZ_ASSERT(value->type() == MIRType::Object || + value->type() == MIRType::Null || + value->type() == MIRType::Value); + MOZ_ASSERT(!value->mightBeType(MIRType::Undefined), + "MToObjectOrNull slow path is invalid for unboxed objects"); + store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj, + elementsOffset, preBarrier); + break; + + default: + MOZ_CRASH(); + } + + current->add(store); + return store; +} + bool IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name, MDefinition* value, -- cgit v1.2.3 From af69cb07db0d810a1a1a507b890e6beb23dc421c Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Sun, 23 Feb 2020 14:41:40 +0100 Subject: Revert #1137 - Remove unboxed arrays - accounting for removal of watch()/unwatch() - updated for intermediate code changes. --- js/src/jit/IonBuilder.cpp | 201 +++++++++++++++++++++++++++++++--------------- 1 file changed, 135 insertions(+), 66 deletions(-) (limited to 'js/src/jit/IonBuilder.cpp') diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index f00167d92..a5991cc7b 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -2227,8 +2227,6 @@ IonBuilder::inspectOpcode(JSOp op) // update that stale value. #endif default: - // Any unused opcodes and JSOP_LIMIT will end up here without having - // to explicitly specify break; } @@ -7355,6 +7353,12 @@ IonBuilder::newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, u if (!templateObject) return true; + if (templateObject->is()) { + MOZ_ASSERT(templateObject->as().capacity() >= length); + if (!templateObject->as().hasInlineElements()) + return true; + } + MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT); size_t arraySlots = @@ -7610,6 +7614,7 @@ IonBuilder::jsop_initelem_array() // intializer, and that arrays are marked as non-packed when writing holes // to them during initialization. bool needStub = false; + JSValueType unboxedType = JSVAL_TYPE_MAGIC; if (shouldAbortOnPreliminaryGroups(obj)) { needStub = true; } else if (!obj->resultTypeSet() || @@ -7620,6 +7625,12 @@ IonBuilder::jsop_initelem_array() } else { MOZ_ASSERT(obj->resultTypeSet()->getObjectCount() == 1); TypeSet::ObjectKey* initializer = obj->resultTypeSet()->getObject(0); + if (initializer->clasp() == &UnboxedArrayObject::class_) { + if (initializer->group()->unboxedLayout().nativeGroup()) + needStub = true; + else + unboxedType = initializer->group()->unboxedLayout().elementType(); + } if (value->type() == MIRType::MagicHole) { if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED)) needStub = true; @@ -7639,46 +7650,60 @@ IonBuilder::jsop_initelem_array() return resumeAfter(store); } - return initializeArrayElement(obj, index, value, /* addResumePoint = */ true); + return initializeArrayElement(obj, index, value, unboxedType, /* addResumePoint = */ true); } bool IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value, + JSValueType unboxedType, bool addResumePointAndIncrementInitializedLength) { MConstant* id = MConstant::New(alloc(), Int32Value(index)); current->add(id); // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj); + MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); current->add(elements); - if (NeedsPostBarrier(value)) - current->add(MPostWriteBarrier::New(alloc(), obj, value)); + if (unboxedType != JSVAL_TYPE_MAGIC) { + // Note: storeUnboxedValue takes care of any post barriers on the value. + storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false); - if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) || - (obj->isNullarySharedStub() && - obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles)) - { - MInstruction* valueDouble = MToDouble::New(alloc(), value); - current->add(valueDouble); - value = valueDouble; - } + if (addResumePointAndIncrementInitializedLength) { + MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj); + current->add(increment); - // Store the value. - MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, + if (!resumeAfter(increment)) + return false; + } + } else { + if (NeedsPostBarrier(value)) + current->add(MPostWriteBarrier::New(alloc(), obj, value)); + + if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) || + (obj->isNullarySharedStub() && + obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles)) + { + MInstruction* valueDouble = MToDouble::New(alloc(), value); + current->add(valueDouble); + value = valueDouble; + } + + // Store the value. + MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, /* needsHoleCheck = */ false); - current->add(store); + current->add(store); - if (addResumePointAndIncrementInitializedLength) { - // Update the initialized length. (The template object for this - // array has the array's ultimate length, so the length field is - // already correct: no updating needed.) - MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); - current->add(initLength); + if (addResumePointAndIncrementInitializedLength) { + // Update the initialized length. (The template object for this + // array has the array's ultimate length, so the length field is + // already correct: no updating needed.) + MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); + current->add(initLength); - if (!resumeAfter(initLength)) - return false; + if (!resumeAfter(initLength)) + return false; + } } return true; @@ -8167,7 +8192,8 @@ IonBuilder::maybeMarkEmpty(MDefinition* ins) static bool ClassHasEffectlessLookup(const Class* clasp) { - return IsTypedObjectClass(clasp) || + return (clasp == &UnboxedArrayObject::class_) || + IsTypedObjectClass(clasp) || (clasp->isNative() && !clasp->getOpsLookupProperty()); } @@ -9441,9 +9467,12 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index) { MOZ_ASSERT(*emitted == false); - if (!ElementAccessIsDenseNative(constraints(), obj, index)) { - trackOptimizationOutcome(TrackedOutcome::AccessNotDense); - return true; + JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, index); + if (unboxedType == JSVAL_TYPE_MAGIC) { + if (!ElementAccessIsDenseNative(constraints(), obj, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); + return true; + } } // Don't generate a fast path if there have been bounds check failures @@ -9460,7 +9489,7 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index) return true; } - if (!jsop_getelem_dense(obj, index)) + if (!jsop_getelem_dense(obj, index, unboxedType)) return false; trackOptimizationSuccess(); @@ -9812,7 +9841,7 @@ IonBuilder::computeHeapType(const TemporaryTypeSet* objTypes, const jsid id) } bool -IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) +IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType) { TemporaryTypeSet* types = bytecodeTypes(pc); @@ -9836,7 +9865,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) !ElementAccessHasExtraIndexedProperty(this, obj); MIRType knownType = MIRType::Value; - if (barrier == BarrierKind::NoBarrier) + if (unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier) knownType = GetElemKnownType(needsHoleCheck, types); // Ensure index is an integer. @@ -9845,13 +9874,13 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) index = idInt32; // Get the elements vector. - MInstruction* elements = MElements::New(alloc(), obj); + MInstruction* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); current->add(elements); // Note: to help GVN, use the original MElements instruction and not // MConvertElementsToDoubles as operand. This is fine because converting // elements to double does not change the initialized length. - MInstruction* initLength = initializedLength(obj, elements); + MInstruction* initLength = initializedLength(obj, elements, unboxedType); // If we can load the element as a definite double, make sure to check that // the array has been converted to homogenous doubles first. @@ -9867,6 +9896,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) } bool loadDouble = + unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier && loopDepth_ && inBounds && @@ -9885,13 +9915,18 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) // hoisting. index = addBoundsCheck(index, initLength); - load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble); - current->add(load); + if (unboxedType != JSVAL_TYPE_MAGIC) { + load = loadUnboxedValue(elements, 0, index, unboxedType, barrier, types); + } else { + load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble); + current->add(load); + } } else { // This load may return undefined, so assume that we *can* read holes, // or that we can read out-of-bounds accesses. In this case, the bounds // check is part of the opcode. - load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck); + load = MLoadElementHole::New(alloc(), elements, index, initLength, + unboxedType, needsHoleCheck); current->add(load); // If maybeUndefined was true, the typeset must have undefined, and @@ -9901,7 +9936,8 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) } if (knownType != MIRType::Value) { - load->setResultType(knownType); + if (unboxedType == JSVAL_TYPE_MAGIC) + load->setResultType(knownType); load->setResultTypeSet(types); } @@ -10348,9 +10384,12 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, { MOZ_ASSERT(*emitted == false); - if (!ElementAccessIsDenseNative(constraints(), object, index)) { - trackOptimizationOutcome(TrackedOutcome::AccessNotDense); - return true; + JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index); + if (unboxedType == JSVAL_TYPE_MAGIC) { + if (!ElementAccessIsDenseNative(constraints(), object, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); + return true; + } } if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, @@ -10384,7 +10423,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, } // Emit dense setelem variant. - if (!jsop_setelem_dense(conversion, object, index, value, writeHole, emitted)) + if (!jsop_setelem_dense(conversion, object, index, value, unboxedType, writeHole, emitted)) return false; if (!*emitted) { @@ -10474,11 +10513,13 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object, bool IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, MDefinition* obj, MDefinition* id, MDefinition* value, - bool writeHole, bool* emitted) + JSValueType unboxedType, bool writeHole, bool* emitted) { MOZ_ASSERT(*emitted == false); - MIRType elementType = DenseNativeElementType(constraints(), obj); + MIRType elementType = MIRType::None; + if (unboxedType == JSVAL_TYPE_MAGIC) + elementType = DenseNativeElementType(constraints(), obj); bool packed = ElementAccessIsPacked(constraints(), obj); // Writes which are on holes in the object do not have to bail out if they @@ -10508,7 +10549,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false); // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj); + MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); current->add(elements); // Ensure the value is a double, if double conversion might be needed. @@ -10545,7 +10586,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, MInstruction* store; MStoreElementCommon* common = nullptr; if (writeHole && hasNoExtraIndexedProperty && !mayBeFrozen) { - MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue); + MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType); store = ins; common = ins; @@ -10557,23 +10598,27 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, bool strict = IsStrictSetPC(pc); MFallibleStoreElement* ins = MFallibleStoreElement::New(alloc(), obj, elements, id, - newValue, strict); + newValue, unboxedType, strict); store = ins; common = ins; current->add(ins); current->push(value); } else { - MInstruction* initLength = initializedLength(obj, elements); + MInstruction* initLength = initializedLength(obj, elements, unboxedType); id = addBoundsCheck(id, initLength); bool needsHoleCheck = !packed && !hasNoExtraIndexedProperty; - MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck); - store = ins; - common = ins; + if (unboxedType != JSVAL_TYPE_MAGIC) { + store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue); + } else { + MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck); + store = ins; + common = ins; - current->add(store); + current->add(store); + } current->push(value); } @@ -10691,6 +10736,18 @@ IonBuilder::jsop_length_fastPath() return true; } + // Compute the length for unboxed array objects. + if (UnboxedArrayElementType(constraints(), obj, nullptr) != JSVAL_TYPE_MAGIC && + !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW)) + { + current->pop(); + + MUnboxedArrayLength* length = MUnboxedArrayLength::New(alloc(), obj); + current->add(length); + current->push(length); + return true; + } + // Compute the length for array typed objects. TypedObjectPrediction prediction = typedObjectPrediction(obj); if (!prediction.isUseless()) { @@ -13675,8 +13732,11 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id) if (shouldAbortOnPreliminaryGroups(obj)) return true; - if (!ElementAccessIsDenseNative(constraints(), obj, id)) - return true; + JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id); + if (unboxedType == JSVAL_TYPE_MAGIC) { + if (!ElementAccessIsDenseNative(constraints(), obj, id)) + return true; + } if (ElementAccessHasExtraIndexedProperty(this, obj)) return true; @@ -13691,10 +13751,10 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id) id = idInt32; // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj); + MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); current->add(elements); - MInstruction* initLength = initializedLength(obj, elements); + MInstruction* initLength = initializedLength(obj, elements, unboxedType); // If there are no holes, speculate the InArray check will not fail. if (!needsHoleCheck && !failedBoundsCheck_) { @@ -13704,7 +13764,8 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id) } // Check if id < initLength and elem[id] not a hole. - MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck); + MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck, + unboxedType); current->add(ins); current->push(ins); @@ -14382,24 +14443,32 @@ IonBuilder::constantInt(int32_t i) } MInstruction* -IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements) +IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements, JSValueType unboxedType) { - MInstruction* res = MInitializedLength::New(alloc(), elements); + MInstruction* res; + if (unboxedType != JSVAL_TYPE_MAGIC) + res = MUnboxedArrayInitializedLength::New(alloc(), obj); + else + res = MInitializedLength::New(alloc(), elements); current->add(res); return res; } MInstruction* -IonBuilder::setInitializedLength(MDefinition* obj, size_t count) +IonBuilder::setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count) { MOZ_ASSERT(count); - // MSetInitializedLength takes the index of the last element, rather - // than the count itself. - MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false); - current->add(elements); - MInstruction* res = - MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1))); + MInstruction* res; + if (unboxedType != JSVAL_TYPE_MAGIC) { + res = MSetUnboxedArrayInitializedLength::New(alloc(), obj, constant(Int32Value(count))); + } else { + // MSetInitializedLength takes the index of the last element, rather + // than the count itself. + MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false); + current->add(elements); + res = MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1))); + } current->add(res); return res; } -- cgit v1.2.3 From 0d1eea2ebfcf1a3746ff0125a6fa340e8b90d722 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Sun, 23 Feb 2020 19:43:47 +0100 Subject: Revert #1091 Remove unboxed object code phase 1 + extras. This should be the last code backout for this. merging this branch should get us back to the way we were (+ additional code changes for later changes) as fasr as the unused unboxed code is concerned. --- js/src/jit/IonBuilder.cpp | 272 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 259 insertions(+), 13 deletions(-) (limited to 'js/src/jit/IonBuilder.cpp') diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index a5991cc7b..f08baf865 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -32,6 +32,7 @@ #include "vm/EnvironmentObject-inl.h" #include "vm/NativeObject-inl.h" #include "vm/ObjectGroup-inl.h" +#include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::jit; @@ -6400,7 +6401,7 @@ IonBuilder::createThisScriptedSingleton(JSFunction* target, MDefinition* callee) JSObject* templateObject = inspector->getTemplateObject(pc); if (!templateObject) return nullptr; - if (!templateObject->is()) + if (!templateObject->is() && !templateObject->is()) return nullptr; if (templateObject->staticPrototype() != proto) return nullptr; @@ -6437,7 +6438,7 @@ IonBuilder::createThisScriptedBaseline(MDefinition* callee) JSObject* templateObject = inspector->getTemplateObject(pc); if (!templateObject) return nullptr; - if (!templateObject->is()) + if (!templateObject->is() && !templateObject->is()) return nullptr; Shape* shape = target->lookupPure(compartment->runtime()->names().prototype); @@ -7734,6 +7735,8 @@ IonBuilder::jsop_initprop(PropertyName* name) if (templateObject->is()) { if (!templateObject->as().containsPure(name)) useSlowPath = true; + } else { + MOZ_ASSERT(templateObject->as().layout().lookup(name)); } } else { useSlowPath = true; @@ -8192,7 +8195,8 @@ IonBuilder::maybeMarkEmpty(MDefinition* ins) static bool ClassHasEffectlessLookup(const Class* clasp) { - return (clasp == &UnboxedArrayObject::class_) || + return (clasp == &UnboxedPlainObject::class_) || + (clasp == &UnboxedArrayObject::class_) || IsTypedObjectClass(clasp) || (clasp->isNative() && !clasp->getOpsLookupProperty()); } @@ -9023,6 +9027,8 @@ IonBuilder::jsop_getelem() } obj = maybeUnboxForPropertyAccess(obj); + if (obj->type() == MIRType::Object) + obj = convertUnboxedObjects(obj); bool emitted = false; @@ -10146,7 +10152,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()); @@ -10981,8 +10987,11 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_ } // Definite slots will always be fixed slots when they are in the - // allowable range for fixed slots. + // allowable range for fixed slots, except for objects which were + // converted from unboxed objects and have a smaller allocation size. size_t nfixed = NativeObject::MAX_FIXED_SLOTS; + if (ObjectGroup* group = key->group()->maybeOriginalUnboxedGroup()) + nfixed = gc::GetGCKindSlots(group->unboxedLayout().getAllocKind()); uint32_t propertySlot = property.maybeTypes()->definiteSlot(); if (slot == UINT32_MAX) { @@ -11039,6 +11048,8 @@ IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name, JSValu return UINT32_MAX; } + key->watchStateChangeForUnboxedConvertedToNative(constraints()); + if (offset == UINT32_MAX) { offset = property->offset; *punboxedType = property->type; @@ -11500,6 +11511,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); @@ -11571,6 +11584,11 @@ IonBuilder::jsop_getprop(PropertyName* name) if (!getPropTryDefiniteSlot(&emitted, obj, name, barrier, types) || emitted) return emitted; + // Try to emit loads from unboxed objects. + trackOptimizationAttempt(TrackedStrategy::GetProp_Unboxed); + if (!getPropTryUnboxed(&emitted, obj, name, barrier, types) || emitted) + return emitted; + // Try to inline a common property getter, or make a call. trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter); if (!getPropTryCommonGetter(&emitted, obj, name, types) || emitted) @@ -11936,6 +11954,49 @@ 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) @@ -12086,14 +12147,45 @@ IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset, return load; } +bool +IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name, + BarrierKind barrier, TemporaryTypeSet* types) +{ + MOZ_ASSERT(*emitted == false); + + JSValueType unboxedType; + uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType); + if (offset == UINT32_MAX) + return true; + + if (obj->type() != MIRType::Object) { + MGuardObject* guard = MGuardObject::New(alloc(), obj); + current->add(guard); + obj = guard; + } + + MInstruction* load = loadUnboxedProperty(obj, offset, unboxedType, barrier, types); + current->push(load); + + if (!pushTypeBarrier(load, types, barrier)) + return false; + + trackOptimizationSuccess(); + *emitted = true; + return true; +} + 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); @@ -12117,8 +12209,10 @@ 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)) + &globalShape, &isOwnProperty, + receivers, convertUnboxedGroups)) { return true; } @@ -12134,7 +12228,8 @@ 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, isOwnProperty); + receivers, convertUnboxedGroups, + isOwnProperty); if (!obj) return false; } @@ -12302,12 +12397,15 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName MOZ_ASSERT(*emitted == false); BaselineInspector::ReceiverVector receivers(alloc()); - if (!inspector->maybeInfoForPropertyOp(pc, receivers)) + BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); + if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) return false; if (!canInlinePropertyOpShapes(receivers)) return true; + obj = convertUnboxedObjects(obj, convertUnboxedGroups); + MIRType rvalType = types->getKnownMIRType(); if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType)) rvalType = MIRType::Value; @@ -12330,6 +12428,45 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName return true; } + if (receivers[0].shape) { + // Monomorphic load from an unboxed object expando. + spew("Inlining monomorphic unboxed expando GETPROP"); + + obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); + obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard); + + MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj); + current->add(expando); + + expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard); + + Shape* shape = receivers[0].shape->searchLinear(NameToId(name)); + MOZ_ASSERT(shape); + + if (!loadSlot(expando, shape, rvalType, barrier, types)) + return false; + + trackOptimizationOutcome(TrackedOutcome::Monomorphic); + *emitted = true; + return true; + } + + // Monomorphic load from an unboxed object. + ObjectGroup* group = receivers[0].group; + if (obj->resultTypeSet() && !obj->resultTypeSet()->hasType(TypeSet::ObjectType(group))) + return true; + + obj = addGroupGuard(obj, group, Bailout_ShapeGuard); + + const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name); + MInstruction* load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types); + current->push(load); + + if (!pushTypeBarrier(load, types, barrier)) + return false; + + trackOptimizationOutcome(TrackedOutcome::Monomorphic); + *emitted = true; return true; } @@ -12571,7 +12708,7 @@ bool IonBuilder::jsop_setprop(PropertyName* name) { MDefinition* value = current->pop(); - MDefinition* obj = current->pop(); + MDefinition* obj = convertUnboxedObjects(current->pop()); bool emitted = false; startTrackingOptimizations(); @@ -12604,6 +12741,13 @@ IonBuilder::jsop_setprop(PropertyName* name) bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value, /* canModify = */ true); + if (!forceInlineCaches()) { + // Try to emit stores to unboxed objects. + trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed); + if (!setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes) || emitted) + return emitted; + } + // Add post barrier if needed. The instructions above manage any post // barriers they need directly. if (NeedsPostBarrier(value)) @@ -12637,8 +12781,10 @@ 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)) + &isOwnProperty, + receivers, convertUnboxedGroups)) { trackOptimizationOutcome(TrackedOutcome::NoProtoFound); return true; @@ -12653,7 +12799,8 @@ 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, isOwnProperty); + receivers, convertUnboxedGroups, + isOwnProperty); if (!obj) return false; } @@ -12969,6 +13116,40 @@ IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t e return store; } +bool +IonBuilder::setPropTryUnboxed(bool* emitted, MDefinition* obj, + PropertyName* name, MDefinition* value, + bool barrier, TemporaryTypeSet* objTypes) +{ + MOZ_ASSERT(*emitted == false); + + if (barrier) { + trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); + return true; + } + + JSValueType unboxedType; + uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType); + if (offset == UINT32_MAX) + return true; + + if (obj->type() != MIRType::Object) { + MGuardObject* guard = MGuardObject::New(alloc(), obj); + current->add(guard); + obj = guard; + } + + MInstruction* store = storeUnboxedProperty(obj, offset, unboxedType, value); + + current->push(value); + + if (!resumeAfter(store)) + return false; + + *emitted = true; + return true; +} + bool IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name, MDefinition* value, @@ -12982,12 +13163,15 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, } BaselineInspector::ReceiverVector receivers(alloc()); - if (!inspector->maybeInfoForPropertyOp(pc, receivers)) + BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); + if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) 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. @@ -13007,6 +13191,46 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, return true; } + if (receivers[0].shape) { + // Monomorphic store to an unboxed object expando. + spew("Inlining monomorphic unboxed expando SETPROP"); + + obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); + obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard); + + MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj); + current->add(expando); + + expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard); + + Shape* shape = receivers[0].shape->searchLinear(NameToId(name)); + MOZ_ASSERT(shape); + + bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name)); + if (!storeSlot(expando, shape, value, needsBarrier)) + return false; + + trackOptimizationOutcome(TrackedOutcome::Monomorphic); + *emitted = true; + return true; + } + + // Monomorphic store to an unboxed object. + spew("Inlining monomorphic unboxed SETPROP"); + + ObjectGroup* group = receivers[0].group; + if (!objTypes->hasType(TypeSet::ObjectType(group))) + return true; + + obj = addGroupGuard(obj, group, Bailout_ShapeGuard); + + const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name); + storeUnboxedProperty(obj, property->offset, property->type, value); + + current->push(value); + + trackOptimizationOutcome(TrackedOutcome::Monomorphic); + *emitted = true; return true; } @@ -13705,7 +13929,7 @@ IonBuilder::jsop_setaliasedvar(EnvironmentCoordinate ec) bool IonBuilder::jsop_in() { - MDefinition* obj = current->pop(); + MDefinition* obj = convertUnboxedObjects(current->pop()); MDefinition* id = current->pop(); bool emitted = false; @@ -14061,6 +14285,19 @@ IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bail return guard; } +MInstruction* +IonBuilder::addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind) +{ + MGuardUnboxedExpando* guard = MGuardUnboxedExpando::New(alloc(), obj, hasExpando, bailoutKind); + current->add(guard); + + // If a shape guard failed in the past, don't optimize group guards. + if (failedShapeGuard_) + guard->setNotMovable(); + + return guard; +} + MInstruction* IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj, const BaselineInspector::ReceiverVector& receivers) @@ -14070,6 +14307,15 @@ IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj, // Monomorphic guard on a native object. return addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard); } + + if (!receivers[0].shape) { + // Guard on an unboxed object that does not have an expando. + obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); + return addUnboxedExpandoGuard(obj, /* hasExpando = */ false, Bailout_ShapeGuard); + } + + // Monomorphic receiver guards are not yet supported when the receiver + // is an unboxed object with an expando. } MGuardReceiverPolymorphic* guard = MGuardReceiverPolymorphic::New(alloc(), obj); -- cgit v1.2.3