diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 22baa228c083..62d036736ca5 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -116,6 +116,7 @@ ConvertAndCopyTo(JSContext *cx, HandleTypeDescr typeObj, HandleTypedObject typedObj, int32_t offset, + HandleAtom name, HandleValue val) { RootedFunction func( @@ -124,14 +125,18 @@ ConvertAndCopyTo(JSContext *cx, return false; InvokeArgs args(cx); - if (!args.init(4)) + if (!args.init(5)) return false; args.setCallee(ObjectValue(*func)); args[0].setObject(*typeObj); args[1].setObject(*typedObj); args[2].setInt32(offset); - args[3].set(val); + if (name) + args[3].setString(name); + else + args[3].setNull(); + args[4].set(val); return Invoke(cx, args); } @@ -140,7 +145,7 @@ static bool ConvertAndCopyTo(JSContext *cx, HandleTypedObject typedObj, HandleValue val) { Rooted type(cx, &typedObj->typeDescr()); - return ConvertAndCopyTo(cx, type, typedObj, 0, val); + return ConvertAndCopyTo(cx, type, typedObj, 0, NullPtr(), val); } /* @@ -1004,6 +1009,8 @@ StructMetaTypeDescr::create(JSContext *cx, if (!LinkConstructorAndPrototype(cx, descr, prototypeObj)) return nullptr; + // Initialize type information for instances of this struct. + return descr; } @@ -1959,7 +1966,8 @@ TypedObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id, size_t offset = descr->fieldOffset(fieldIndex); Rooted fieldType(cx, &descr->fieldDescr(fieldIndex)); - return ConvertAndCopyTo(cx, fieldType, typedObj, offset, vp); + RootedAtom fieldName(cx, &descr->fieldName(fieldIndex)); + return ConvertAndCopyTo(cx, fieldType, typedObj, offset, fieldName, vp); } } @@ -2013,7 +2021,7 @@ TypedObject::obj_setArrayElement(JSContext *cx, Rooted elementType(cx); elementType = &descr->as().elementType(); size_t offset = elementType->size() * index; - return ConvertAndCopyTo(cx, elementType, typedObj, offset, vp); + return ConvertAndCopyTo(cx, elementType, typedObj, offset, NullPtr(), vp); } bool @@ -2822,21 +2830,27 @@ JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreScalar##T::JitInfo, #define JS_STORE_REFERENCE_CLASS_IMPL(_constant, T, _name) \ bool \ -js::StoreReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp) \ +js::StoreReference##T::Func(ThreadSafeContext *cx, unsigned argc, Value *vp) \ { \ CallArgs args = CallArgsFromVp(argc, vp); \ - MOZ_ASSERT(args.length() == 3); \ + MOZ_ASSERT(args.length() == 4); \ MOZ_ASSERT(args[0].isObject() && args[0].toObject().is()); \ MOZ_ASSERT(args[1].isInt32()); \ + MOZ_ASSERT(args[2].isString() || args[2].isNull()); \ \ TypedObject &typedObj = args[0].toObject().as(); \ int32_t offset = args[1].toInt32(); \ \ + jsid id = args[2].isString() \ + ? types::IdToTypeId(AtomToId(&args[2].toString()->asAtom())) \ + : JSID_VOID; \ + \ /* Should be guaranteed by the typed objects API: */ \ MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \ \ T *target = reinterpret_cast(typedObj.typedMem(offset)); \ - store(target, args[2]); \ + if (!store(cx, target, args[3], &typedObj, id)) \ + return false; \ args.rval().setUndefined(); \ return true; \ } \ @@ -2896,24 +2910,54 @@ JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadReference##T::JitInfo, // differs, we abstract it away using specialized variants of the // private methods `store()` and `load()`. -void -StoreReferenceHeapValue::store(HeapValue *heap, const Value &v) +bool +StoreReferenceHeapValue::store(ThreadSafeContext *cx, HeapValue *heap, const Value &v, + TypedObject *obj, jsid id) { + // Undefined values are not included in type inference information for + // value properties of typed objects, as these properties are always + // considered to contain undefined. + if (!v.isUndefined()) { + if (cx->isJSContext()) + types::AddTypePropertyId(cx->asJSContext(), obj, id, v); + else if (!types::HasTypePropertyId(obj, id, v)) + return false; + } + *heap = v; + return true; } -void -StoreReferenceHeapPtrObject::store(HeapPtrObject *heap, const Value &v) +bool +StoreReferenceHeapPtrObject::store(ThreadSafeContext *cx, HeapPtrObject *heap, const Value &v, + TypedObject *obj, jsid id) { MOZ_ASSERT(v.isObjectOrNull()); // or else Store_object is being misused + + // Null pointers are not included in type inference information for + // object properties of typed objects, as these properties are always + // considered to contain null. + if (v.isObject()) { + if (cx->isJSContext()) + types::AddTypePropertyId(cx->asJSContext(), obj, id, v); + else if (!types::HasTypePropertyId(obj, id, v)) + return false; + } + *heap = v.toObjectOrNull(); + return true; } -void -StoreReferenceHeapPtrString::store(HeapPtrString *heap, const Value &v) +bool +StoreReferenceHeapPtrString::store(ThreadSafeContext *cx, HeapPtrString *heap, const Value &v, + TypedObject *obj, jsid id) { MOZ_ASSERT(v.isString()); // or else Store_string is being misused + + // Note: string references are not reflected in type information for the object. *heap = v.toString(); + + return true; } void diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 71a0602bb4c3..ca6bb9f7b68b 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -910,9 +910,9 @@ class StoreScalar##T { \ }; /* - * Usage: Store_Any(targetDatum, targetOffset, value) - * Store_Object(targetDatum, targetOffset, value) - * Store_string(targetDatum, targetOffset, value) + * Usage: Store_Any(targetDatum, targetOffset, fieldName, value) + * Store_Object(targetDatum, targetOffset, fieldName, value) + * Store_string(targetDatum, targetOffset, fieldName, value) * * Intrinsic function. Stores `value` into the memory referenced by * `targetDatum` at the offset `targetOffset`. @@ -920,12 +920,13 @@ class StoreScalar##T { \ * Assumes (and asserts) that: * - `targetDatum` is attached * - `targetOffset` is a valid offset within the bounds of `targetDatum` - * - `value` is an object (`Store_Object`) or string (`Store_string`). + * - `value` is an object or null (`Store_Object`) or string (`Store_string`). */ #define JS_STORE_REFERENCE_CLASS_DEFN(_constant, T, _name) \ class StoreReference##T { \ private: \ - static void store(T* heap, const Value &v); \ + static bool store(ThreadSafeContext *cx, T* heap, const Value &v, \ + TypedObject *obj, jsid id); \ \ public: \ static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \ diff --git a/js/src/builtin/TypedObject.js b/js/src/builtin/TypedObject.js index 47a297637108..2b98b6ca8e7f 100644 --- a/js/src/builtin/TypedObject.js +++ b/js/src/builtin/TypedObject.js @@ -181,7 +181,7 @@ function TypedObjectGetSimd(descr, typedObj, offset) { // Writes `fromValue` into the `typedObj` at offset `offset`, adapting // it to `descr` as needed. This is the most general entry point // and works for any type. -function TypedObjectSet(descr, typedObj, offset, fromValue) { +function TypedObjectSet(descr, typedObj, offset, name, fromValue) { if (!TypedObjectIsAttached(typedObj)) ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); @@ -191,7 +191,7 @@ function TypedObjectSet(descr, typedObj, offset, fromValue) { return; case JS_TYPEREPR_REFERENCE_KIND: - TypedObjectSetReference(descr, typedObj, offset, fromValue); + TypedObjectSetReference(descr, typedObj, offset, name, fromValue); return; case JS_TYPEREPR_SIMD_KIND: @@ -217,7 +217,7 @@ function TypedObjectSet(descr, typedObj, offset, fromValue) { var fieldDescr = fieldDescrs[i]; var fieldOffset = fieldOffsets[i]; var fieldValue = fromValue[fieldName]; - TypedObjectSet(fieldDescr, typedObj, offset + fieldOffset, fieldValue); + TypedObjectSet(fieldDescr, typedObj, offset + fieldOffset, fieldName, fieldValue); } return; } @@ -241,7 +241,7 @@ function TypedObjectSetArray(descr, length, typedObj, offset, fromValue) { var elemSize = DESCR_SIZE(elemDescr); var elemOffset = offset; for (var i = 0; i < length; i++) { - TypedObjectSet(elemDescr, typedObj, elemOffset, fromValue[i]); + TypedObjectSet(elemDescr, typedObj, elemOffset, null, fromValue[i]); elemOffset += elemSize; } } @@ -293,18 +293,18 @@ function TypedObjectSetScalar(descr, typedObj, offset, fromValue) { return undefined; } -function TypedObjectSetReference(descr, typedObj, offset, fromValue) { +function TypedObjectSetReference(descr, typedObj, offset, name, fromValue) { var type = DESCR_TYPE(descr); switch (type) { case JS_REFERENCETYPEREPR_ANY: - return Store_Any(typedObj, offset, fromValue); + return Store_Any(typedObj, offset, name, fromValue); case JS_REFERENCETYPEREPR_OBJECT: var value = (fromValue === null ? fromValue : ToObject(fromValue)); - return Store_Object(typedObj, offset, value); + return Store_Object(typedObj, offset, name, value); case JS_REFERENCETYPEREPR_STRING: - return Store_string(typedObj, offset, ToString(fromValue)); + return Store_string(typedObj, offset, name, ToString(fromValue)); } assert(false, "Unhandled scalar type: " + type); @@ -351,6 +351,7 @@ function TypedObjectSetSimd(descr, typedObj, offset, fromValue) { function ConvertAndCopyTo(destDescr, destTypedObj, destOffset, + fieldName, fromValue) { assert(IsObject(destDescr) && ObjectIsTypeDescr(destDescr), @@ -361,7 +362,7 @@ function ConvertAndCopyTo(destDescr, if (!TypedObjectIsAttached(destTypedObj)) ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); - TypedObjectSet(destDescr, destTypedObj, destOffset, fromValue); + TypedObjectSet(destDescr, destTypedObj, destOffset, fieldName, fromValue); } // Wrapper for use from C++ code. @@ -815,7 +816,7 @@ function BuildTypedSeqImpl(arrayType, len, depth, func) { var r = callFunction(std_Function_apply, func, undefined, indices); callFunction(std_Array_pop, indices); if (r !== undefined) - TypedObjectSet(grainType, result, outOffset, r); // result[...indices] = r; + TypedObjectSet(grainType, result, outOffset, null, r); // result[...indices] = r; // Increment indices. IncrementIterationSpace(indices, iterationSpace); @@ -918,7 +919,7 @@ function MapUntypedSeqImpl(inArray, outputType, maybeFunc) { var r = func(element, i, inArray, out); if (r !== undefined) - TypedObjectSet(outGrainType, result, outOffset, r); // result[i] = r + TypedObjectSet(outGrainType, result, outOffset, null, r); // result[i] = r } // Update offset and (implicitly) increment indices. @@ -978,7 +979,7 @@ function MapTypedSeqImpl(inArray, depth, outputType, func) { // Invoke: var r = func(element, ...indices, collection, out); var r = func(element, i, inArray, out); if (r !== undefined) - TypedObjectSet(outGrainType, result, outOffset, r); // result[i] = r + TypedObjectSet(outGrainType, result, outOffset, null, r); // result[i] = r // Update offsets and (implicitly) increment indices. inOffset += inUnitSize; @@ -1013,7 +1014,7 @@ function MapTypedSeqImpl(inArray, depth, outputType, func) { callFunction(std_Array_push, args, inArray, out); var r = callFunction(std_Function_apply, func, void 0, args); if (r !== undefined) - TypedObjectSet(outGrainType, result, outOffset, r); // result[...indices] = r + TypedObjectSet(outGrainType, result, outOffset, null, r); // result[...indices] = r // Update offsets and explicitly increment indices. inOffset += inUnitSize; diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 60b48b2d3f1e..38836f6549e4 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -9109,13 +9109,34 @@ CodeGenerator::visitLoadUnboxedPointerT(LLoadUnboxedPointerT *lir) const LAllocation *index = lir->index(); Register out = ToRegister(lir->output()); - if (index->isConstant()) { - int32_t offset = ToInt32(index) * sizeof(uintptr_t) + lir->mir()->offsetAdjustment(); - masm.loadPtr(Address(elements, offset), out); + bool bailOnNull; + int32_t offsetAdjustment; + if (lir->mir()->isLoadUnboxedObjectOrNull()) { + MOZ_ASSERT(lir->mir()->toLoadUnboxedObjectOrNull()->bailOnNull()); + bailOnNull = true; + offsetAdjustment = lir->mir()->toLoadUnboxedObjectOrNull()->offsetAdjustment(); + } else if (lir->mir()->isLoadUnboxedString()) { + bailOnNull = false; + offsetAdjustment = lir->mir()->toLoadUnboxedString()->offsetAdjustment(); } else { - masm.loadPtr(BaseIndex(elements, ToRegister(index), ScalePointer, - lir->mir()->offsetAdjustment()), out); + MOZ_CRASH(); } + + if (index->isConstant()) { + Address source(elements, ToInt32(index) * sizeof(uintptr_t) + offsetAdjustment); + masm.loadPtr(source, out); + } else { + BaseIndex source(elements, ToRegister(index), ScalePointer, offsetAdjustment); + masm.loadPtr(source, out); + } + + if (bailOnNull) { + Label bail; + masm.branchTestPtr(Assembler::Zero, out, out, &bail); + if (!bailoutFrom(&bail, lir->snapshot())) + return false; + } + return true; } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 9883ff779df8..8052e7ac6c68 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -7043,7 +7043,7 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name) return jsop_setprop(name); } - if (!CanWriteProperty(constraints(), property, value)) + if (!CanWriteProperty(alloc(), constraints(), property, value)) return jsop_setprop(name); current->pop(); @@ -7371,7 +7371,7 @@ IonBuilder::getElemTryReferenceElemOfTypedObject(bool *emitted, *emitted = true; - return pushReferenceLoadFromTypedObject(obj, indexAsByteOffset, elemType); + return pushReferenceLoadFromTypedObject(obj, indexAsByteOffset, elemType, nullptr); } bool @@ -7418,7 +7418,8 @@ IonBuilder::pushScalarLoadFromTypedObject(MDefinition *obj, bool IonBuilder::pushReferenceLoadFromTypedObject(MDefinition *typedObj, const LinearSum &byteOffset, - ReferenceTypeDescr::Type type) + ReferenceTypeDescr::Type type, + PropertyName *name) { // Find location within the owner object. MDefinition *elements, *scaledOffset; @@ -7429,22 +7430,34 @@ IonBuilder::pushReferenceLoadFromTypedObject(MDefinition *typedObj, types::TemporaryTypeSet *observedTypes = bytecodeTypes(pc); MInstruction *load; - BarrierKind barrier = BarrierKind::NoBarrier; + BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), + typedObj, name, observedTypes); + switch (type) { - case ReferenceTypeDescr::TYPE_ANY: + case ReferenceTypeDescr::TYPE_ANY: { + // Make sure the barrier reflects the possibility of reading undefined. + bool bailOnUndefined = barrier == BarrierKind::NoBarrier && + !observedTypes->hasType(types::Type::UndefinedType()); + if (bailOnUndefined) + barrier = BarrierKind::TypeTagOnly; load = MLoadElement::New(alloc(), elements, scaledOffset, false, false, adjustment); - if (!observedTypes->unknown()) - barrier = BarrierKind::TypeSet; break; - case ReferenceTypeDescr::TYPE_OBJECT: - load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, adjustment); - if (!observedTypes->unknownObject() || !observedTypes->hasType(types::Type::NullType())) - barrier = BarrierKind::TypeSet; + } + case ReferenceTypeDescr::TYPE_OBJECT: { + // Make sure the barrier reflects the possibility of reading null. When + // there is no other barrier needed we include the null bailout with + // MLoadUnboxedObjectOrNull, which avoids the need to box the result + // for a type barrier instruction. + bool bailOnNull = barrier == BarrierKind::NoBarrier && + !observedTypes->hasType(types::Type::NullType()); + load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, bailOnNull, adjustment); break; - case ReferenceTypeDescr::TYPE_STRING: + } + case ReferenceTypeDescr::TYPE_STRING: { load = MLoadUnboxedString::New(alloc(), elements, scaledOffset, adjustment); observedTypes->addType(types::Type::StringType(), alloc().lifoAlloc()); break; + } } current->add(load); @@ -8264,8 +8277,8 @@ IonBuilder::setElemTryReferenceElemOfTypedObject(bool *emitted, if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset)) return true; - if (!storeReferenceTypedObjectValue(obj, indexAsByteOffset, elemType, value)) - return false; + if (!storeReferenceTypedObjectValue(obj, indexAsByteOffset, elemType, value, nullptr)) + return true; current->push(value); @@ -9274,7 +9287,7 @@ IonBuilder::jsop_getprop(PropertyName *name) return emitted; // Try to emit loads from known binary data blocks - if (!getPropTryTypedObject(&emitted, obj, name, types) || emitted) + if (!getPropTryTypedObject(&emitted, obj, name) || emitted) return emitted; // Try to emit loads from definite slots. @@ -9443,8 +9456,7 @@ IonBuilder::getPropTryConstant(bool *emitted, MDefinition *obj, PropertyName *na bool IonBuilder::getPropTryTypedObject(bool *emitted, MDefinition *obj, - PropertyName *name, - types::TemporaryTypeSet *resultTypes) + PropertyName *name) { TypedObjectPrediction fieldPrediction; size_t fieldOffset; @@ -9463,22 +9475,20 @@ IonBuilder::getPropTryTypedObject(bool *emitted, obj, fieldOffset, fieldPrediction, - fieldIndex, - resultTypes); + fieldIndex); case type::Reference: return getPropTryReferencePropOfTypedObject(emitted, obj, fieldOffset, fieldPrediction, - resultTypes); + name); case type::Scalar: return getPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset, - fieldPrediction, - resultTypes); + fieldPrediction); } MOZ_CRASH("Bad kind"); @@ -9487,8 +9497,7 @@ IonBuilder::getPropTryTypedObject(bool *emitted, bool IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedObj, int32_t fieldOffset, - TypedObjectPrediction fieldPrediction, - types::TemporaryTypeSet *resultTypes) + TypedObjectPrediction fieldPrediction) { // Must always be loading the same scalar type Scalar::Type fieldType = fieldPrediction.scalarType(); @@ -9511,7 +9520,7 @@ bool IonBuilder::getPropTryReferencePropOfTypedObject(bool *emitted, MDefinition *typedObj, int32_t fieldOffset, TypedObjectPrediction fieldPrediction, - types::TemporaryTypeSet *resultTypes) + PropertyName *name) { ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType(); @@ -9525,7 +9534,7 @@ IonBuilder::getPropTryReferencePropOfTypedObject(bool *emitted, MDefinition *typ if (!byteOffset.add(fieldOffset)) setForceAbort(); - return pushReferenceLoadFromTypedObject(typedObj, byteOffset, fieldType); + return pushReferenceLoadFromTypedObject(typedObj, byteOffset, fieldType, name); } bool @@ -9533,8 +9542,7 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted, MDefinition *typedObj, int32_t fieldOffset, TypedObjectPrediction fieldPrediction, - size_t fieldIndex, - types::TemporaryTypeSet *resultTypes) + size_t fieldIndex) { // Don't optimize if the typed object might be neutered. types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global()); @@ -10022,14 +10030,14 @@ IonBuilder::jsop_setprop(PropertyName *name) if (!setPropTryCommonSetter(&emitted, obj, name, value) || emitted) return emitted; - types::TemporaryTypeSet *objTypes = obj->resultTypeSet(); - bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value, - /* canModify = */ true); - // Try to emit stores to known binary data blocks if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted) return emitted; + types::TemporaryTypeSet *objTypes = obj->resultTypeSet(); + bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value, + /* canModify = */ true); + if (!barrier) { // Try to emit store from definite slots. if (!setPropTryDefiniteSlot(&emitted, obj, name, value, objTypes) || emitted) @@ -10177,7 +10185,7 @@ IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj, case type::Reference: return setPropTryReferencePropOfTypedObject(emitted, obj, fieldOffset, - value, fieldPrediction); + value, fieldPrediction, name); case type::Scalar: return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset, @@ -10196,7 +10204,8 @@ IonBuilder::setPropTryReferencePropOfTypedObject(bool *emitted, MDefinition *obj, int32_t fieldOffset, MDefinition *value, - TypedObjectPrediction fieldPrediction) + TypedObjectPrediction fieldPrediction, + PropertyName *name) { ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType(); @@ -10208,8 +10217,8 @@ IonBuilder::setPropTryReferencePropOfTypedObject(bool *emitted, if (!byteOffset.add(fieldOffset)) setForceAbort(); - if (!storeReferenceTypedObjectValue(obj, byteOffset, fieldType, value)) - return false; + if (!storeReferenceTypedObjectValue(obj, byteOffset, fieldType, value, name)) + return true; current->push(value); @@ -11390,8 +11399,24 @@ bool IonBuilder::storeReferenceTypedObjectValue(MDefinition *typedObj, const LinearSum &byteOffset, ReferenceTypeDescr::Type type, - MDefinition *value) + MDefinition *value, + PropertyName *name) { + // Make sure we aren't adding new type information for writes of object and value + // references. + if (type != ReferenceTypeDescr::TYPE_STRING) { + MOZ_ASSERT(type == ReferenceTypeDescr::TYPE_ANY || + type == ReferenceTypeDescr::TYPE_OBJECT); + MIRType implicitType = + (type == ReferenceTypeDescr::TYPE_ANY) ? MIRType_Undefined : MIRType_Null; + + if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &typedObj, name, &value, + /* canModify = */ true, implicitType)) + { + return false; + } + } + // Find location within the owner object. MDefinition *elements, *scaledOffset; int32_t adjustment; diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 415f5be49138..a480b8200093 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -421,21 +421,18 @@ class IonBuilder types::TemporaryTypeSet *types); bool getPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName *name, BarrierKind barrier, types::TemporaryTypeSet *types); - bool getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName *name, - types::TemporaryTypeSet *resultTypes); + bool getPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName *name); bool getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedObj, int32_t fieldOffset, - TypedObjectPrediction fieldTypeReprs, - types::TemporaryTypeSet *resultTypes); + TypedObjectPrediction fieldTypeReprs); bool getPropTryReferencePropOfTypedObject(bool *emitted, MDefinition *typedObj, int32_t fieldOffset, TypedObjectPrediction fieldPrediction, - types::TemporaryTypeSet *resultTypes); + PropertyName *name); bool getPropTryComplexPropOfTypedObject(bool *emitted, MDefinition *typedObj, int32_t fieldOffset, TypedObjectPrediction fieldTypeReprs, - size_t fieldIndex, - types::TemporaryTypeSet *resultTypes); + size_t fieldIndex); bool getPropTryInnerize(bool *emitted, MDefinition *obj, PropertyName *name, types::TemporaryTypeSet *types); bool getPropTryCache(bool *emitted, MDefinition *obj, PropertyName *name, @@ -460,7 +457,8 @@ class IonBuilder MDefinition *obj, int32_t fieldOffset, MDefinition *value, - TypedObjectPrediction fieldPrediction); + TypedObjectPrediction fieldPrediction, + PropertyName *name); bool setPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *obj, int32_t fieldOffset, @@ -494,7 +492,8 @@ class IonBuilder bool storeReferenceTypedObjectValue(MDefinition *typedObj, const LinearSum &byteOffset, ReferenceTypeDescr::Type type, - MDefinition *value); + MDefinition *value, + PropertyName *name); bool storeScalarTypedObjectValue(MDefinition *typedObj, const LinearSum &byteOffset, ScalarTypeDescr::Type type, @@ -515,7 +514,8 @@ class IonBuilder ScalarTypeDescr::Type type); bool pushReferenceLoadFromTypedObject(MDefinition *typedObj, const LinearSum &byteOffset, - ReferenceTypeDescr::Type type); + ReferenceTypeDescr::Type type, + PropertyName *name); MDefinition *neuterCheck(MDefinition *obj); // jsop_setelem() helpers. diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 54b287b4fdaa..1938b9a483f2 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -4568,8 +4568,9 @@ class LLoadUnboxedPointerT : public LInstructionHelper<1, 2, 0> setOperand(1, index); } - const MLoadUnboxedString *mir() const { - return mir_->toLoadUnboxedString(); + MDefinition *mir() { + MOZ_ASSERT(mir_->isLoadUnboxedObjectOrNull() || mir_->isLoadUnboxedString()); + return mir_; } const LAllocation *elements() { return getOperand(0); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 44149607f58b..6af66d0ca582 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2739,7 +2739,17 @@ LIRGenerator::visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *ins) { MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment())); MOZ_ASSERT(ins->index()->type() == MIRType_Int32); + + if (ins->type() == MIRType_Object) { + LLoadUnboxedPointerT *lir = new(alloc()) LLoadUnboxedPointerT(useRegister(ins->elements()), + useRegisterOrConstant(ins->index())); + if (ins->bailOnNull() && !assignSnapshot(lir, Bailout_TypeBarrierO)) + return false; + return define(lir, ins); + } + MOZ_ASSERT(ins->type() == MIRType_Value); + MOZ_ASSERT(!ins->bailOnNull()); LLoadUnboxedPointerV *lir = new(alloc()) LLoadUnboxedPointerV(useRegister(ins->elements()), useRegisterOrConstant(ins->index())); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 7c9727a86669..64f005dfddda 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -4274,10 +4274,30 @@ jit::AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name, } } +static bool +PropertyTypeIncludes(TempAllocator &alloc, types::HeapTypeSetKey property, + MDefinition *value, MIRType implicitType) +{ + // If implicitType is not MIRType_None, it is an additional type which the + // property implicitly includes. In this case, make a new type set which + // explicitly contains the type. + types::TypeSet *types = property.maybeTypes(); + if (implicitType != MIRType_None) { + types::Type newType = types::Type::PrimitiveType(ValueTypeFromMIRType(implicitType)); + if (types) + types = types->clone(alloc.lifoAlloc()); + else + types = alloc.lifoAlloc()->new_(); + types->addType(newType, alloc.lifoAlloc()); + } + + return TypeSetIncludes(types, value->type(), value->resultTypeSet()); +} + static bool TryAddTypeBarrierForWrite(TempAllocator &alloc, types::CompilerConstraintList *constraints, MBasicBlock *current, types::TemporaryTypeSet *objTypes, - PropertyName *name, MDefinition **pvalue) + PropertyName *name, MDefinition **pvalue, MIRType implicitType) { // Return whether pvalue was modified to include a type barrier ensuring // that writing the value to objTypes/id will not require changing type @@ -4301,7 +4321,7 @@ TryAddTypeBarrierForWrite(TempAllocator &alloc, types::CompilerConstraintList *c if (!property.maybeTypes() || property.couldBeConstant(constraints)) return false; - if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) + if (PropertyTypeIncludes(alloc, property, *pvalue, implicitType)) return false; // This freeze is not required for correctness, but ensures that we @@ -4384,18 +4404,20 @@ AddTypeGuard(TempAllocator &alloc, MBasicBlock *current, MDefinition *obj, // Whether value can be written to property without changing type information. bool -jit::CanWriteProperty(types::CompilerConstraintList *constraints, - types::HeapTypeSetKey property, MDefinition *value) +jit::CanWriteProperty(TempAllocator &alloc, types::CompilerConstraintList *constraints, + types::HeapTypeSetKey property, MDefinition *value, + MIRType implicitType /* = MIRType_None */) { if (property.couldBeConstant(constraints)) return false; - return TypeSetIncludes(property.maybeTypes(), value->type(), value->resultTypeSet()); + return PropertyTypeIncludes(alloc, property, value, implicitType); } bool jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints, MBasicBlock *current, MDefinition **pobj, - PropertyName *name, MDefinition **pvalue, bool canModify) + PropertyName *name, MDefinition **pvalue, + bool canModify, MIRType implicitType) { // If any value being written is not reflected in the type information for // objects which obj could represent, a type barrier is needed when writing @@ -4425,14 +4447,15 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstrai jsid id = name ? NameToId(name) : JSID_VOID; types::HeapTypeSetKey property = object->property(id); - if (!CanWriteProperty(constraints, property, *pvalue)) { + if (!CanWriteProperty(alloc, constraints, property, *pvalue, implicitType)) { // Either pobj or pvalue needs to be modified to filter out the // types which the value could have but are not in the property, // or a VM call is required. A VM call is always required if pobj // and pvalue cannot be modified. if (!canModify) return true; - success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue); + success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue, + implicitType); break; } } @@ -4457,7 +4480,7 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstrai jsid id = name ? NameToId(name) : JSID_VOID; types::HeapTypeSetKey property = object->property(id); - if (CanWriteProperty(constraints, property, *pvalue)) + if (CanWriteProperty(alloc, constraints, property, *pvalue, implicitType)) continue; if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index feb408324b19..5977b3aa496e 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -7931,13 +7931,21 @@ class MLoadUnboxedObjectOrNull : public MBinaryInstruction, public SingleObjectPolicy::Data { + bool bailOnNull_; int32_t offsetAdjustment_; - MLoadUnboxedObjectOrNull(MDefinition *elements, MDefinition *index, int32_t offsetAdjustment) + MLoadUnboxedObjectOrNull(MDefinition *elements, MDefinition *index, + bool bailOnNull, int32_t offsetAdjustment) : MBinaryInstruction(elements, index), + bailOnNull_(bailOnNull), offsetAdjustment_(offsetAdjustment) { - setResultType(MIRType_Value); + if (bailOnNull) { + // Don't eliminate loads which bail out on a null pointer, for the + // same reason as MLoadElement. + setGuard(); + } + setResultType(bailOnNull ? MIRType_Object : MIRType_Value); setMovable(); MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment)); MOZ_ASSERT(index->type() == MIRType_Int32); @@ -7948,8 +7956,8 @@ class MLoadUnboxedObjectOrNull static MLoadUnboxedObjectOrNull *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, - int32_t offsetAdjustment) { - return new(alloc) MLoadUnboxedObjectOrNull(elements, index, offsetAdjustment); + bool bailOnNull, int32_t offsetAdjustment) { + return new(alloc) MLoadUnboxedObjectOrNull(elements, index, bailOnNull, offsetAdjustment); } MDefinition *elements() const { @@ -7958,13 +7966,21 @@ class MLoadUnboxedObjectOrNull MDefinition *index() const { return getOperand(1); } + bool bailOnNull() const { + return bailOnNull_; + } int32_t offsetAdjustment() const { return offsetAdjustment_; } + bool fallible() const { + return bailOnNull(); + } bool congruentTo(const MDefinition *ins) const { if (!ins->isLoadUnboxedObjectOrNull()) return false; const MLoadUnboxedObjectOrNull *other = ins->toLoadUnboxedObjectOrNull(); + if (bailOnNull() != other->bailOnNull()) + return false; if (offsetAdjustment() != other->offsetAdjustment()) return false; return congruentIfOperandsEqual(other); @@ -12503,12 +12519,13 @@ bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, MDefinition *obj, PropertyName *name); void AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name, types::TemporaryTypeSet *observed); -bool CanWriteProperty(types::CompilerConstraintList *constraints, - types::HeapTypeSetKey property, MDefinition *value); +bool CanWriteProperty(TempAllocator &alloc, types::CompilerConstraintList *constraints, + types::HeapTypeSetKey property, MDefinition *value, + MIRType implicitType = MIRType_None); bool PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints, MBasicBlock *current, MDefinition **pobj, PropertyName *name, MDefinition **pvalue, - bool canModify); + bool canModify, MIRType implicitType = MIRType_None); } // namespace jit } // namespace js diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index b61bcd88a5b2..8513f5252e46 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -1137,9 +1137,12 @@ struct TypeObject : public gc::TenuredCell * * The type sets in the properties of a type object describe the possible * values that can be read out of that property in actual JS objects. - * Properties only account for native properties (those with a slot and no - * specialized getter hook) and the elements of dense arrays. For accesses - * on such properties, the correspondence is as follows: + * In native objects, property types account for plain data properties + * (those with a slot and no getter or setter hook) and dense elements. + * In typed objects, property types account for object and value properties + * and elements in the object. + * + * For accesses on these properties, the correspondence is as follows: * * 1. If the type has unknownProperties(), the possible properties and * value types for associated JSObjects are unknown. @@ -1148,16 +1151,21 @@ struct TypeObject : public gc::TenuredCell * which is a property in obj, before obj->getProperty(id) the property * in type for id must reflect the result of the getProperty. * - * There is an exception for properties of global JS objects which - * are undefined at the point where the property was (lazily) generated. - * In such cases the property type set will remain empty, and the - * 'undefined' type will only be added after a subsequent assignment or - * deletion. After these properties have been assigned a defined value, - * the only way they can become undefined again is after such an assign - * or deletion. + * There are several exceptions to this: * - * There is another exception for array lengths, which are special cased - * by the compiler and VM and are not reflected in property types. + * 1. For properties of global JS objects which are undefined at the point + * where the property was (lazily) generated, the property type set will + * remain empty, and the 'undefined' type will only be added after a + * subsequent assignment or deletion. After these properties have been + * assigned a defined value, the only way they can become undefined + * again is after such an assign or deletion. + * + * 2. Array lengths are special cased by the compiler and VM and are not + * reflected in property types. + * + * 3. In typed objects, the initial values of properties (null pointers and + * undefined values) are not reflected in the property types. These + * values are always possible when reading the property. * * We establish these by using write barriers on calls to setProperty and * defineProperty which are on native properties, and on any jitcode which