author | Jan de Mooij <jdemooij@mozilla.com> |
Fri, 20 Jan 2017 09:58:02 +0100 | |
changeset 330294 | a16c508abfd042dfcabe141a108bcd44a48d2dee |
parent 330293 | d21bd74f1a20d20afcefb23be322c7f20763e1a8 |
child 330295 | 4d870873f2b76157d57bcff8447140f7c0f7f877 |
push id | 31236 |
push user | cbook@mozilla.com |
push date | Fri, 20 Jan 2017 14:26:03 +0000 |
treeherder | mozilla-central@3cedab21a7e6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | evilpie |
bugs | 1326067 |
milestone | 53.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -800,16 +800,108 @@ BaselineCacheIRCompiler::emitStoreUnboxe /* failure = */ nullptr); if (UnboxedTypeNeedsPostBarrier(fieldType)) BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_); return true; } bool +BaselineCacheIRCompiler::emitStoreTypedObjectReferenceProperty() +{ + ObjOperandId objId = reader.objOperandId(); + Address offsetAddr = stubAddress(reader.stubOffset()); + TypedThingLayout layout = reader.typedThingLayout(); + ReferenceTypeDescr::Type type = reader.referenceTypeDescrType(); + + // Allocate the fixed registers first. These need to be fixed for + // callTypeUpdateIC. + AutoStubFrame stubFrame(*this); + AutoScratchRegister scratch1(allocator, masm, R1.scratchReg()); + ValueOperand val = allocator.useFixedValueRegister(masm, reader.valOperandId(), R0); + + Register obj = allocator.useRegister(masm, objId); + + // We don't need a type update IC if the property is always a string. + if (type != ReferenceTypeDescr::TYPE_STRING) { + LiveGeneralRegisterSet saveRegs; + saveRegs.add(obj); + saveRegs.add(val); + if (!callTypeUpdateIC(stubFrame, obj, val, scratch1, saveRegs)) + return false; + } + + // Compute the address being written to. + LoadTypedThingData(masm, layout, obj, scratch1); + masm.addPtr(offsetAddr, scratch1); + Address dest(scratch1, 0); + + // To avoid running out of registers on x86, use ICStubReg as second + // scratch. It won't be used after this. + Register scratch2 = ICStubReg; + + switch (type) { + case ReferenceTypeDescr::TYPE_ANY: + EmitPreBarrier(masm, dest, MIRType::Value); + masm.storeValue(val, dest); + break; + + case ReferenceTypeDescr::TYPE_OBJECT: { + EmitPreBarrier(masm, dest, MIRType::Object); + Label isNull, done; + masm.branchTestObject(Assembler::NotEqual, val, &isNull); + masm.unboxObject(val, scratch2); + masm.storePtr(scratch2, dest); + masm.jump(&done); + masm.bind(&isNull); + masm.storePtr(ImmWord(0), dest); + masm.bind(&done); + break; + } + + case ReferenceTypeDescr::TYPE_STRING: + EmitPreBarrier(masm, dest, MIRType::String); + masm.unboxString(val, scratch2); + masm.storePtr(scratch2, dest); + break; + } + + if (type != ReferenceTypeDescr::TYPE_STRING) + BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch1, LiveGeneralRegisterSet(), cx_); + + return true; +} + +bool +BaselineCacheIRCompiler::emitStoreTypedObjectScalarProperty() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + Address offsetAddr = stubAddress(reader.stubOffset()); + TypedThingLayout layout = reader.typedThingLayout(); + Scalar::Type type = reader.scalarType(); + ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId()); + AutoScratchRegister scratch1(allocator, masm); + AutoScratchRegister scratch2(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + // Compute the address being written to. + LoadTypedThingData(masm, layout, obj, scratch1); + masm.addPtr(offsetAddr, scratch1); + Address dest(scratch1, 0); + + BaselineStoreToTypedArray(cx_, masm, type, val, dest, scratch2, + failure->label(), failure->label()); + + return true; +} + +bool BaselineCacheIRCompiler::emitTypeMonitorResult() { allocator.discardStack(masm); EmitEnterTypeMonitorIC(masm); return true; } bool
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -279,16 +279,43 @@ DoTypeUpdateFallback(JSContext* cx, Base RootedScript script(cx, frame->script()); RootedObject obj(cx, &objval.toObject()); RootedId id(cx); switch (stub->kind()) { case ICStub::CacheIR_Updated: id = stub->toCacheIR_Updated()->updateStubId(); MOZ_ASSERT(id != JSID_EMPTY); + + // If we're storing null/undefined to a typed object property, check if + // we want to include it in this property's type information. + if (MOZ_UNLIKELY(obj->is<TypedObject>()) && value.isNullOrUndefined()) { + StructTypeDescr* structDescr = &obj->as<TypedObject>().typeDescr().as<StructTypeDescr>(); + size_t fieldIndex; + MOZ_ALWAYS_TRUE(structDescr->fieldIndex(id, &fieldIndex)); + + TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex); + ReferenceTypeDescr::Type type = fieldDescr->as<ReferenceTypeDescr>().type(); + if (type == ReferenceTypeDescr::TYPE_ANY) { + // Ignore undefined values, which are included implicitly in type + // information for this property. + if (value.isUndefined()) + break; + } else { + MOZ_ASSERT(type == ReferenceTypeDescr::TYPE_OBJECT); + + // Ignore null values being written here. Null is included + // implicitly in type information for this property. Note that + // non-object, non-null values are not possible here, these + // should have been filtered out by the IR emitter. + if (value.isNull()) + break; + } + } + AddTypePropertyId(cx, obj, id, value); break; case ICStub::SetElem_DenseOrUnboxedArray: case ICStub::SetElem_DenseOrUnboxedArrayAdd: { id = JSID_VOID; AddTypePropertyId(cx, obj, id, value); break; } @@ -297,35 +324,16 @@ DoTypeUpdateFallback(JSContext* cx, Base jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(script); if (*pc == JSOP_SETALIASEDVAR || *pc == JSOP_INITALIASEDLEXICAL) id = NameToId(EnvironmentCoordinateName(cx->caches.envCoordinateNameCache, script, pc)); else id = NameToId(script->getName(pc)); AddTypePropertyId(cx, obj, id, value); break; } - case ICStub::SetProp_TypedObject: { - MOZ_ASSERT(obj->is<TypedObject>()); - jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(script); - id = NameToId(script->getName(pc)); - if (stub->toSetProp_TypedObject()->isObjectReference()) { - // Ignore all values being written except plain objects. Null - // is included implicitly in type information for this property, - // and non-object non-null values will cause the stub to fail to - // match shortly and we will end up doing the assignment in the VM. - if (value.isObject()) - AddTypePropertyId(cx, obj, id, value); - } else { - // Ignore undefined values, which are included implicitly in type - // information for this property. - if (!value.isUndefined()) - AddTypePropertyId(cx, obj, id, value); - } - break; - } default: MOZ_CRASH("Invalid stub"); } return stub->addUpdateStubForValue(cx, script /* = outerScript */, obj, id, value); } typedef bool (*DoTypeUpdateFallbackFn)(JSContext*, BaselineFrame*, ICUpdatedStub*, HandleValue, @@ -1851,23 +1859,21 @@ ICSetElemDenseOrUnboxedArrayAddCompiler: EmitStubGuardFailure(masm); return true; } // // SetElem_TypedArray // -// Write an arbitrary value to a typed array or typed object address at dest. -// If the value could not be converted to the appropriate format, jump to -// failure or failureModifiedScratch. -template <typename T> -static void -StoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type, Address value, T dest, - Register scratch, Label* failure, Label* failureModifiedScratch) +template <typename S, typename T> +void +BaselineStoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type, const S& value, + const T& dest, Register scratch, Label* failure, + Label* failureModifiedScratch) { Label done; if (type == Scalar::Float32 || type == Scalar::Float64) { masm.ensureDouble(value, FloatReg0, failure); if (type == Scalar::Float32) { masm.convertDoubleToFloat32(FloatReg0, ScratchFloat32Reg); masm.storeToTypedFloatArray(type, ScratchFloat32Reg, dest); @@ -1917,16 +1923,26 @@ StoreToTypedArray(JSContext* cx, MacroAs } else { masm.jump(failure); } } masm.bind(&done); } +template void +BaselineStoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type, + const ValueOperand& value, const Address& dest, Register scratch, + Label* failure, Label* failureModifiedScratch); + +template void +BaselineStoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type, + const Address& value, const BaseIndex& dest, Register scratch, + Label* failure, Label* failureModifiedScratch); + bool ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm) { MOZ_ASSERT(engine_ == Engine::Baseline); Label failure; if (layout_ != Layout_TypedArray) @@ -1979,18 +1995,18 @@ ICSetElem_TypedArray::Compiler::generate // R0 or R1, as long as it's restored before jumping to the next stub. regs = availableGeneralRegs(0); regs.takeUnchecked(obj); regs.takeUnchecked(key); regs.take(scratchReg); Register secondScratch = regs.takeAny(); Label failureModifiedSecondScratch; - StoreToTypedArray(cx, masm, type_, value, dest, - secondScratch, &failure, &failureModifiedSecondScratch); + BaselineStoreToTypedArray(cx, masm, type_, value, dest, + secondScratch, &failure, &failureModifiedSecondScratch); EmitReturnFromIC(masm); if (failureModifiedSecondScratch.used()) { // Writing to secondScratch may have clobbered R0 or R1, restore them // first. masm.bind(&failureModifiedSecondScratch); masm.tagValue(JSVAL_TYPE_OBJECT, obj, R0); masm.tagValue(JSVAL_TYPE_INT32, key, R1); @@ -2744,58 +2760,16 @@ TryAttachSetAccessorPropStub(JSContext* *attached = true; return true; } return true; } static bool -TryAttachTypedObjectSetPropStub(JSContext* cx, HandleScript script, - ICSetProp_Fallback* stub, HandleId id, - HandleObject obj, HandleValue rhs, bool* attached) -{ - MOZ_ASSERT(!*attached); - - if (!cx->runtime()->jitSupportsFloatingPoint) - return true; - - if (!obj->is<TypedObject>()) - return true; - - if (!obj->as<TypedObject>().typeDescr().is<StructTypeDescr>()) - return true; - Rooted<StructTypeDescr*> structDescr(cx); - structDescr = &obj->as<TypedObject>().typeDescr().as<StructTypeDescr>(); - - size_t fieldIndex; - if (!structDescr->fieldIndex(id, &fieldIndex)) - return true; - - Rooted<TypeDescr*> fieldDescr(cx, &structDescr->fieldDescr(fieldIndex)); - if (!fieldDescr->is<SimpleTypeDescr>()) - return true; - - uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex); - - ICSetProp_TypedObject::Compiler compiler(cx, obj->maybeShape(), obj->group(), fieldOffset, - &fieldDescr->as<SimpleTypeDescr>()); - ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs)) - return false; - - stub->addNewStub(newStub); - - *attached = true; - return true; -} - -static bool DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_, Value* stack, HandleValue lhs, HandleValue rhs) { // This fallback stub may trigger debug mode toggling. DebugModeOSRVolatileStub<ICSetProp_Fallback*> stub(frame, stub_); RootedScript script(cx, frame->script()); jsbytecode* pc = stub->icEntry()->pc(script); @@ -2927,25 +2901,16 @@ DoSetPropFallback(JSContext* cx, Baselin !TryAttachSetValuePropStub(cx, script, pc, stub, obj, oldShape, oldGroup, name, id, rhs, &attached)) { return false; } if (attached) return true; - if (!attached && - lhs.isObject() && - !TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached)) - { - return false; - } - if (attached) - return true; - MOZ_ASSERT(!attached); if (!isTemporarilyUnoptimizable) stub->noteUnoptimizableAccess(); return true; } typedef bool (*DoSetPropFallbackFn)(JSContext*, BaselineFrame*, ICSetProp_Fallback*, Value*, @@ -3193,138 +3158,16 @@ ICSetPropNativeAddCompiler::generateStub EmitUnstowICValues(masm, 2); masm.bind(&failure); EmitStubGuardFailure(masm); return true; } bool -ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - - CheckForTypedObjectWithDetachedStorage(cx, masm, &failure); - - // Guard input is an object. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); - Register scratch = regs.takeAny(); - - // Unbox and shape guard. - Register object = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(ICStubReg, ICSetProp_TypedObject::offsetOfShape()), scratch); - masm.branchTestObjShape(Assembler::NotEqual, object, scratch, &failure); - - // Guard that the object group matches. - masm.loadPtr(Address(ICStubReg, ICSetProp_TypedObject::offsetOfGroup()), scratch); - masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch, - &failure); - - if (needsUpdateStubs()) { - // Stow both R0 and R1 (object and value). - EmitStowICValues(masm, 2); - - // Move RHS into R0 for TypeUpdate check. - masm.moveValue(R1, R0); - - // Call the type update stub. - if (!callTypeUpdateIC(masm, sizeof(Value))) - return false; - - // Unstow R0 and R1 (object and key) - EmitUnstowICValues(masm, 2); - - // We may have clobbered object in the TypeUpdate IC. Rederive it. - masm.unboxObject(R0, object); - - // Trigger post barriers here on the values being written. Descriptors - // which can write objects also need update stubs. - LiveGeneralRegisterSet saveRegs; - saveRegs.add(R0); - saveRegs.add(R1); - saveRegs.addUnchecked(object); - saveRegs.add(ICStubReg); - BaselineEmitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs, cx); - } - - // Save the rhs on the stack so we can get a second scratch register. - Label failurePopRHS; - masm.pushValue(R1); - regs = availableGeneralRegs(1); - regs.takeUnchecked(object); - regs.take(scratch); - Register secondScratch = regs.takeAny(); - - // Get the object's data pointer. - LoadTypedThingData(masm, layout_, object, scratch); - - // Compute the address being written to. - masm.load32(Address(ICStubReg, ICSetProp_TypedObject::offsetOfFieldOffset()), secondScratch); - masm.addPtr(secondScratch, scratch); - - Address dest(scratch, 0); - Address value(masm.getStackPointer(), 0); - - if (fieldDescr_->is<ScalarTypeDescr>()) { - Scalar::Type type = fieldDescr_->as<ScalarTypeDescr>().type(); - StoreToTypedArray(cx, masm, type, value, dest, - secondScratch, &failurePopRHS, &failurePopRHS); - masm.popValue(R1); - } else { - ReferenceTypeDescr::Type type = fieldDescr_->as<ReferenceTypeDescr>().type(); - - masm.popValue(R1); - - switch (type) { - case ReferenceTypeDescr::TYPE_ANY: - EmitPreBarrier(masm, dest, MIRType::Value); - masm.storeValue(R1, dest); - break; - - case ReferenceTypeDescr::TYPE_OBJECT: { - EmitPreBarrier(masm, dest, MIRType::Object); - Label notObject; - masm.branchTestObject(Assembler::NotEqual, R1, ¬Object); - Register rhsObject = masm.extractObject(R1, ExtractTemp0); - masm.storePtr(rhsObject, dest); - EmitReturnFromIC(masm); - masm.bind(¬Object); - masm.branchTestNull(Assembler::NotEqual, R1, &failure); - masm.storePtr(ImmWord(0), dest); - break; - } - - case ReferenceTypeDescr::TYPE_STRING: { - EmitPreBarrier(masm, dest, MIRType::String); - masm.branchTestString(Assembler::NotEqual, R1, &failure); - Register rhsString = masm.extractString(R1, ExtractTemp0); - masm.storePtr(rhsString, dest); - break; - } - - default: - MOZ_CRASH(); - } - } - - EmitReturnFromIC(masm); - - masm.bind(&failurePopRHS); - masm.popValue(R1); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) { MOZ_ASSERT(engine_ == Engine::Baseline); Label failure; Label failureUnstow; Label failureLeaveStubFrame;
--- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -1230,104 +1230,16 @@ class ICSetPropNativeAddCompiler : publi return newStub<ICSetProp_NativeAddImpl<ProtoChainDepth>>( space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_); } ICUpdatedStub* getStub(ICStubSpace* space); }; -class ICSetProp_TypedObject : public ICUpdatedStub -{ - friend class ICStubSpace; - - GCPtrShape shape_; - GCPtrObjectGroup group_; - uint32_t fieldOffset_; - bool isObjectReference_; - - ICSetProp_TypedObject(JitCode* stubCode, Shape* shape, ObjectGroup* group, - uint32_t fieldOffset, bool isObjectReference) - : ICUpdatedStub(ICStub::SetProp_TypedObject, stubCode), - shape_(shape), - group_(group), - fieldOffset_(fieldOffset), - isObjectReference_(isObjectReference) - { - (void) fieldOffset_; // Silence clang warning - } - - public: - GCPtrShape& shape() { - return shape_; - } - GCPtrObjectGroup& group() { - return group_; - } - bool isObjectReference() { - return isObjectReference_; - } - - static size_t offsetOfShape() { - return offsetof(ICSetProp_TypedObject, shape_); - } - static size_t offsetOfGroup() { - return offsetof(ICSetProp_TypedObject, group_); - } - static size_t offsetOfFieldOffset() { - return offsetof(ICSetProp_TypedObject, fieldOffset_); - } - - class Compiler : public ICStubCompiler { - protected: - RootedShape shape_; - RootedObjectGroup group_; - uint32_t fieldOffset_; - TypedThingLayout layout_; - Rooted<SimpleTypeDescr*> fieldDescr_; - - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - return static_cast<int32_t>(engine_) | - (static_cast<int32_t>(kind) << 1) | - (static_cast<int32_t>(SimpleTypeDescrKey(fieldDescr_)) << 17) | - (static_cast<int32_t>(layout_) << 25); - } - - public: - Compiler(JSContext* cx, Shape* shape, ObjectGroup* group, uint32_t fieldOffset, - SimpleTypeDescr* fieldDescr) - : ICStubCompiler(cx, ICStub::SetProp_TypedObject, Engine::Baseline), - shape_(cx, shape), - group_(cx, group), - fieldOffset_(fieldOffset), - layout_(GetTypedThingLayout(shape->getObjectClass())), - fieldDescr_(cx, fieldDescr) - {} - - ICUpdatedStub* getStub(ICStubSpace* space) { - bool isObjectReference = - fieldDescr_->is<ReferenceTypeDescr>() && - fieldDescr_->as<ReferenceTypeDescr>().type() == ReferenceTypeDescr::TYPE_OBJECT; - ICUpdatedStub* stub = newStub<ICSetProp_TypedObject>(space, getStubCode(), shape_, - group_, fieldOffset_, - isObjectReference); - if (!stub || !stub->initUpdatingChain(cx, space)) - return nullptr; - return stub; - } - - bool needsUpdateStubs() { - return fieldDescr_->is<ReferenceTypeDescr>() && - fieldDescr_->as<ReferenceTypeDescr>().type() != ReferenceTypeDescr::TYPE_STRING; - } - }; -}; - // Base stub for calling a setters on a native or unboxed object. class ICSetPropCallSetter : public ICStub { friend class ICStubSpace; protected: // Shape/group of receiver object. Used for both own and proto setters. HeapReceiverGuard receiverGuard_; @@ -2529,12 +2441,21 @@ IsCacheableDOMProxy(JSObject* obj) return handler->family() == GetDOMProxyHandlerFamily(); } struct IonOsrTempData; void EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, const BaseIndex& address, JSValueType type); +// Write an arbitrary value to a typed array or typed object address at dest. +// If the value could not be converted to the appropriate format, jump to +// failure or failureModifiedScratch. +template <typename S, typename T> +void +BaselineStoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type, const S& value, + const T& dest, Register scratch, Label* failure, + Label* failureModifiedScratch); + } // namespace jit } // namespace js #endif /* jit_BaselineIC_h */
--- a/js/src/jit/BaselineICList.h +++ b/js/src/jit/BaselineICList.h @@ -65,17 +65,16 @@ namespace jit { \ _(BindName_Fallback) \ \ _(GetIntrinsic_Fallback) \ _(GetIntrinsic_Constant) \ \ _(SetProp_Fallback) \ _(SetProp_NativeAdd) \ - _(SetProp_TypedObject) \ _(SetProp_CallScripted) \ _(SetProp_CallNative) \ \ _(TableSwitch) \ \ _(IteratorNew_Fallback) \ _(IteratorMore_Fallback) \ _(IteratorMore_Native) \
--- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -1562,16 +1562,18 @@ SetPropIRGenerator::tryAttachStub() ObjOperandId objId = writer.guardIsObject(lhsValId); if (nameOrSymbol) { if (tryAttachNativeSetSlot(obj, objId, id, rhsValId)) return true; if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId)) return true; if (tryAttachUnboxedProperty(obj, objId, id, rhsValId)) return true; + if (tryAttachTypedObjectProperty(obj, objId, id, rhsValId)) + return true; } return false; } return false; } static void @@ -1694,8 +1696,66 @@ SetPropIRGenerator::tryAttachUnboxedProp UnboxedPlainObject::offsetOfData() + property->offset, rhsId); writer.returnFromIC(); setUpdateStubInfo(id); preliminaryObjectAction_ = PreliminaryObjectAction::Unlink; return true; } + +bool +SetPropIRGenerator::tryAttachTypedObjectProperty(HandleObject obj, ObjOperandId objId, HandleId id, + ValOperandId rhsId) +{ + if (!obj->is<TypedObject>() || !cx_->runtime()->jitSupportsFloatingPoint) + return false; + + if (cx_->compartment()->detachedTypedObjects) + return false; + + if (!obj->as<TypedObject>().typeDescr().is<StructTypeDescr>()) + return false; + + StructTypeDescr* structDescr = &obj->as<TypedObject>().typeDescr().as<StructTypeDescr>(); + size_t fieldIndex; + if (!structDescr->fieldIndex(id, &fieldIndex)) + return false; + + TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex); + if (!fieldDescr->is<SimpleTypeDescr>()) + return false; + + uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex); + TypedThingLayout layout = GetTypedThingLayout(obj->getClass()); + + writer.guardNoDetachedTypedObjects(); + writer.guardShape(objId, obj->as<TypedObject>().shape()); + writer.guardGroup(objId, obj->group()); + + setUpdateStubInfo(id); + + // Scalar types can always be stored without a type update stub. + if (fieldDescr->is<ScalarTypeDescr>()) { + Scalar::Type type = fieldDescr->as<ScalarTypeDescr>().type(); + writer.storeTypedObjectScalarProperty(objId, fieldOffset, layout, type, rhsId); + writer.returnFromIC(); + return true; + } + + // For reference types, guard on the RHS type first, so that + // StoreTypedObjectReferenceProperty is infallible. + ReferenceTypeDescr::Type type = fieldDescr->as<ReferenceTypeDescr>().type(); + switch (type) { + case ReferenceTypeDescr::TYPE_ANY: + break; + case ReferenceTypeDescr::TYPE_OBJECT: + writer.guardIsObjectOrNull(rhsId); + break; + case ReferenceTypeDescr::TYPE_STRING: + writer.guardType(rhsId, JSVAL_TYPE_STRING); + break; + } + + writer.storeTypedObjectReferenceProperty(objId, fieldOffset, layout, type, rhsId); + writer.returnFromIC(); + return true; +}
--- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -167,16 +167,18 @@ enum class CacheKind : uint8_t /* See CacheIR.cpp 'DOM proxies' comment. */ \ _(LoadDOMExpandoValue) \ _(LoadDOMExpandoValueGuardGeneration) \ _(LoadDOMExpandoValueIgnoreGeneration)\ _(GuardDOMExpandoMissingOrGuardShape) \ \ _(StoreFixedSlot) \ _(StoreDynamicSlot) \ + _(StoreTypedObjectReferenceProperty) \ + _(StoreTypedObjectScalarProperty) \ _(StoreUnboxedProperty) \ \ /* The *Result ops load a value into the cache's result register. */ \ _(LoadFixedSlotResult) \ _(LoadDynamicSlotResult) \ _(LoadUnboxedPropertyResult) \ _(LoadTypedObjectResult) \ _(LoadDenseElementResult) \ @@ -562,16 +564,35 @@ class MOZ_RAII CacheIRWriter : public JS addStubField(offset, StubField::Type::RawWord); writeOperandId(rhs); } void storeDynamicSlot(ObjOperandId obj, size_t offset, ValOperandId rhs) { writeOpWithOperandId(CacheOp::StoreDynamicSlot, obj); addStubField(offset, StubField::Type::RawWord); writeOperandId(rhs); } + void storeTypedObjectReferenceProperty(ObjOperandId obj, uint32_t offset, + TypedThingLayout layout, ReferenceTypeDescr::Type type, + ValOperandId rhs) + { + writeOpWithOperandId(CacheOp::StoreTypedObjectReferenceProperty, obj); + addStubField(offset, StubField::Type::RawWord); + buffer_.writeByte(uint32_t(layout)); + buffer_.writeByte(uint32_t(type)); + writeOperandId(rhs); + } + void storeTypedObjectScalarProperty(ObjOperandId obj, uint32_t offset, TypedThingLayout layout, + Scalar::Type type, ValOperandId rhs) + { + writeOpWithOperandId(CacheOp::StoreTypedObjectScalarProperty, obj); + addStubField(offset, StubField::Type::RawWord); + buffer_.writeByte(uint32_t(layout)); + buffer_.writeByte(uint32_t(type)); + writeOperandId(rhs); + } void storeUnboxedProperty(ObjOperandId obj, JSValueType type, size_t offset, ValOperandId rhs) { writeOpWithOperandId(CacheOp::StoreUnboxedProperty, obj); buffer_.writeByte(uint32_t(type)); addStubField(offset, StubField::Type::RawWord); writeOperandId(rhs); } @@ -708,16 +729,20 @@ class MOZ_RAII CacheIRReader uint32_t stubOffset() { return buffer_.readByte() * sizeof(uintptr_t); } GuardClassKind guardClassKind() { return GuardClassKind(buffer_.readByte()); } JSValueType valueType() { return JSValueType(buffer_.readByte()); } TypedThingLayout typedThingLayout() { return TypedThingLayout(buffer_.readByte()); } Scalar::Type scalarType() { return Scalar::Type(buffer_.readByte()); } uint32_t typeDescrKey() { return buffer_.readByte(); } JSWhyMagic whyMagic() { return JSWhyMagic(buffer_.readByte()); } + ReferenceTypeDescr::Type referenceTypeDescrType() { + return ReferenceTypeDescr::Type(buffer_.readByte()); + } + bool matchOp(CacheOp op) { const uint8_t* pos = buffer_.currentPosition(); if (readOp() == op) return true; buffer_.seek(pos, 0); return false; } bool matchOp(CacheOp op, OperandId id) { @@ -875,16 +900,18 @@ class MOZ_RAII SetPropIRGenerator : publ } bool tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId); bool tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId); bool tryAttachUnboxedProperty(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId); + bool tryAttachTypedObjectProperty(HandleObject obj, ObjOperandId objId, HandleId id, + ValOperandId rhsId); public: SetPropIRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind, bool* isTemporarilyUnoptimizable, HandleValue lhsVal, HandleValue idVal, HandleValue rhsVal); bool tryAttachStub();
--- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -851,16 +851,28 @@ IonCacheIRCompiler::emitStoreDynamicSlot bool IonCacheIRCompiler::emitStoreUnboxedProperty() { MOZ_CRASH("Baseline-specific op"); } bool +IonCacheIRCompiler::emitStoreTypedObjectReferenceProperty() +{ + MOZ_CRASH("Baseline-specific op"); +} + +bool +IonCacheIRCompiler::emitStoreTypedObjectScalarProperty() +{ + MOZ_CRASH("Baseline-specific op"); +} + +bool IonCacheIRCompiler::emitLoadTypedObjectResult() { AutoOutputRegister output(*this); Register obj = allocator.useRegister(masm, reader.objOperandId()); AutoScratchRegister scratch1(allocator, masm); AutoScratchRegister scratch2(allocator, masm); TypedThingLayout layout = reader.typedThingLayout();
--- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -367,22 +367,16 @@ ICStub::trace(JSTracer* trc) case 1: propStub->toImpl<1>()->traceShapes(trc); break; case 2: propStub->toImpl<2>()->traceShapes(trc); break; case 3: propStub->toImpl<3>()->traceShapes(trc); break; case 4: propStub->toImpl<4>()->traceShapes(trc); break; default: MOZ_CRASH("Invalid proto stub."); } break; } - case ICStub::SetProp_TypedObject: { - ICSetProp_TypedObject* propStub = toSetProp_TypedObject(); - TraceEdge(trc, &propStub->shape(), "baseline-setprop-typedobject-stub-shape"); - TraceEdge(trc, &propStub->group(), "baseline-setprop-typedobject-stub-group"); - break; - } case ICStub::SetProp_CallScripted: { ICSetProp_CallScripted* callStub = toSetProp_CallScripted(); callStub->receiverGuard().trace(trc); TraceEdge(trc, &callStub->holder(), "baseline-setpropcallscripted-stub-holder"); TraceEdge(trc, &callStub->holderShape(), "baseline-setpropcallscripted-stub-holdershape"); TraceEdge(trc, &callStub->setter(), "baseline-setpropcallscripted-stub-setter"); break; }
--- a/js/src/jit/SharedIC.h +++ b/js/src/jit/SharedIC.h @@ -2378,17 +2378,17 @@ class ICGetProp_Generic : public ICMonit {} ICStub* getStub(ICStubSpace* space) { return newStub<ICGetProp_Generic>(space, getStubCode(), firstMonitorStub_); } }; }; -static uint32_t +static inline uint32_t SimpleTypeDescrKey(SimpleTypeDescr* descr) { if (descr->is<ScalarTypeDescr>()) return uint32_t(descr->as<ScalarTypeDescr>().type()) << 1; return (uint32_t(descr->as<ReferenceTypeDescr>().type()) << 1) | 1; } inline bool