diff options
Diffstat (limited to 'js/src/jit/IonBuilder.cpp')
-rw-r--r-- | js/src/jit/IonBuilder.cpp | 201 |
1 files changed, 135 insertions, 66 deletions
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<UnboxedArrayObject>()) { + MOZ_ASSERT(templateObject->as<UnboxedArrayObject>().capacity() >= length); + if (!templateObject->as<UnboxedArrayObject>().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; } |