diff options
Diffstat (limited to 'js/src/jit/IonCaches.cpp')
-rw-r--r-- | js/src/jit/IonCaches.cpp | 336 |
1 files changed, 333 insertions, 3 deletions
diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index fb4291188..f5e4659c1 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -31,6 +31,7 @@ #include "jit/shared/Lowering-shared-inl.h" #include "vm/Interpreter-inl.h" #include "vm/Shape-inl.h" +#include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::jit; @@ -619,7 +620,26 @@ TestMatchingReceiver(MacroAssembler& masm, IonCache::StubAttacher& attacher, Register object, JSObject* obj, Label* failure, bool alwaysCheckGroup = false) { - if (obj->is<TypedObject>()) { + if (obj->is<UnboxedPlainObject>()) { + MOZ_ASSERT(failure); + + masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure); + Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); + if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) { + masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); + Label success; + masm.push(object); + masm.loadPtr(expandoAddress, object); + masm.branchTestObjShape(Assembler::Equal, object, expando->lastProperty(), + &success); + masm.pop(object); + masm.jump(failure); + masm.bind(&success); + masm.pop(object); + } else { + masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure); + } + } else if (obj->is<TypedObject>()) { attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), ImmGCPtr(obj->group()), failure); @@ -736,6 +756,7 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, // jump directly. Otherwise, jump to the end of the stub, so there's a // common point to patch. bool multipleFailureJumps = (obj != holder) + || obj->is<UnboxedPlainObject>() || (checkTDZ && output.hasValue()) || (failures != nullptr && failures->used()); @@ -754,6 +775,7 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, Register scratchReg = Register::FromCode(0); // Quell compiler warning. if (obj != holder || + obj->is<UnboxedPlainObject>() || !holder->as<NativeObject>().isFixedSlot(shape->slot())) { if (output.hasValue()) { @@ -814,6 +836,10 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, holderReg = InvalidReg; } + } else if (obj->is<UnboxedPlainObject>()) { + holder = obj->as<UnboxedPlainObject>().maybeExpando(); + holderReg = scratchReg; + masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), holderReg); } else { holderReg = object; } @@ -841,6 +867,30 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, attacher.jumpNextStub(masm); } +static void +GenerateReadUnboxed(JSContext* cx, IonScript* ion, MacroAssembler& masm, + IonCache::StubAttacher& attacher, JSObject* obj, + const UnboxedLayout::Property* property, + Register object, TypedOrValueRegister output, + Label* failures = nullptr) +{ + // Guard on the group of the object. + attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, + Address(object, JSObject::offsetOfGroup()), + ImmGCPtr(obj->group()), failures); + + Address address(object, UnboxedPlainObject::offsetOfData() + property->offset); + + masm.loadUnboxedProperty(address, property->type, output); + + attacher.jumpRejoin(masm); + + if (failures) { + masm.bind(failures); + attacher.jumpNextStub(masm); + } +} + static bool EmitGetterCall(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher, JSObject* obj, @@ -1448,6 +1498,67 @@ GetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip } bool +GetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, void* returnAddr, bool* emitted) +{ + MOZ_ASSERT(canAttachStub()); + MOZ_ASSERT(!*emitted); + MOZ_ASSERT(outerScript->ionScript() == ion); + + if (!obj->is<UnboxedPlainObject>()) + return true; + const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id); + if (!property) + return true; + + *emitted = true; + + MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); + + Label failures; + emitIdGuard(masm, id, &failures); + Label* maybeFailures = failures.used() ? &failures : nullptr; + + StubAttacher attacher(*this); + GenerateReadUnboxed(cx, ion, masm, attacher, obj, property, object(), output(), maybeFailures); + return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed", + JS::TrackedOutcome::ICGetPropStub_UnboxedRead); +} + +bool +GetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, void* returnAddr, bool* emitted) +{ + MOZ_ASSERT(canAttachStub()); + MOZ_ASSERT(!*emitted); + MOZ_ASSERT(outerScript->ionScript() == ion); + + if (!obj->is<UnboxedPlainObject>()) + return true; + Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando()); + if (!expando) + return true; + + Shape* shape = expando->lookup(cx, id); + if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) + return true; + + *emitted = true; + + MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); + + Label failures; + emitIdGuard(masm, id, &failures); + Label* maybeFailures = failures.used() ? &failures : nullptr; + + StubAttacher attacher(*this); + GenerateReadSlot(cx, ion, masm, attacher, DontCheckTDZ, obj, obj, + shape, object(), output(), maybeFailures); + return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando", + JS::TrackedOutcome::ICGetPropStub_UnboxedReadExpando); +} + +bool GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted) { @@ -2016,6 +2127,12 @@ GetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, returnAddr, emitted)) return false; + if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, returnAddr, emitted)) + return false; + + if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, returnAddr, emitted)) + return false; + if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, id, emitted)) return false; } @@ -2194,6 +2311,12 @@ GenerateSetSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att NativeObject::slotsSizeMustNotOverflow(); + if (obj->is<UnboxedPlainObject>()) { + obj = obj->as<UnboxedPlainObject>().maybeExpando(); + masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), tempReg); + object = tempReg; + } + if (obj->as<NativeObject>().isFixedSlot(shape->slot())) { Address addr(object, NativeObject::getFixedSlotOffset(shape->slot())); @@ -2831,13 +2954,23 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att masm.branchTestObjGroup(Assembler::NotEqual, object, oldGroup, failures); if (obj->maybeShape()) { masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, failures); + } else { + MOZ_ASSERT(obj->is<UnboxedPlainObject>()); + + Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); + masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failures); + + masm.loadPtr(expandoAddress, tempReg); + masm.branchTestObjShape(Assembler::NotEqual, tempReg, oldShape, failures); } Shape* newShape = obj->maybeShape(); + if (!newShape) + newShape = obj->as<UnboxedPlainObject>().maybeExpando()->lastProperty(); // Guard that the incoming value is in the type set for the property // if a type barrier is required. - if (newShape && checkTypeset) + if (checkTypeset) CheckTypeSetForWrite(masm, obj, newShape->propid(), tempReg, value, failures); // Guard shapes along prototype chain. @@ -2858,7 +2991,9 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att } // Call a stub to (re)allocate dynamic slots, if necessary. - uint32_t newNumDynamicSlots = obj->as<NativeObject>().numDynamicSlots(); + uint32_t newNumDynamicSlots = obj->is<UnboxedPlainObject>() + ? obj->as<UnboxedPlainObject>().maybeExpando()->numDynamicSlots() + : obj->as<NativeObject>().numDynamicSlots(); if (NativeObject::dynamicSlotsCount(oldShape) != newNumDynamicSlots) { AllocatableRegisterSet regs(RegisterSet::Volatile()); LiveRegisterSet save(regs.asLiveSet()); @@ -2869,6 +3004,12 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att Register temp1 = regs.takeAnyGeneral(); Register temp2 = regs.takeAnyGeneral(); + if (obj->is<UnboxedPlainObject>()) { + // Pass the expando object to the stub. + masm.Push(object); + masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object); + } + masm.setupUnalignedABICall(temp1); masm.loadJSContext(temp1); masm.passABIArg(temp1); @@ -2885,16 +3026,27 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att masm.jump(&allocDone); masm.bind(&allocFailed); + if (obj->is<UnboxedPlainObject>()) + masm.Pop(object); masm.PopRegsInMask(save); masm.jump(failures); masm.bind(&allocDone); masm.setFramePushed(framePushedAfterCall); + if (obj->is<UnboxedPlainObject>()) + masm.Pop(object); masm.PopRegsInMask(save); } bool popObject = false; + if (obj->is<UnboxedPlainObject>()) { + masm.push(object); + popObject = true; + obj = obj->as<UnboxedPlainObject>().maybeExpando(); + masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object); + } + // Write the object or expando object's new shape. Address shapeAddr(object, ShapedObject::offsetOfShape()); if (cx->zone()->needsIncrementalBarrier()) @@ -2902,6 +3054,8 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att masm.storePtr(ImmGCPtr(newShape), shapeAddr); if (oldGroup != obj->group()) { + MOZ_ASSERT(!obj->is<UnboxedPlainObject>()); + // Changing object's group from a partially to fully initialized group, // per the acquired properties analysis. Only change the group if the // old group still has a newScript. @@ -3144,6 +3298,141 @@ CanAttachNativeSetProp(JSContext* cx, HandleObject obj, HandleId id, const Const return SetPropertyIC::CanAttachNone; } +static void +GenerateSetUnboxed(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher, + JSObject* obj, jsid id, uint32_t unboxedOffset, JSValueType unboxedType, + Register object, Register tempReg, const ConstantOrRegister& value, + bool checkTypeset, Label* failures) +{ + // Guard on the type of the object. + masm.branchPtr(Assembler::NotEqual, + Address(object, JSObject::offsetOfGroup()), + ImmGCPtr(obj->group()), failures); + + if (checkTypeset) + CheckTypeSetForWrite(masm, obj, id, tempReg, value, failures); + + Address address(object, UnboxedPlainObject::offsetOfData() + unboxedOffset); + + if (cx->zone()->needsIncrementalBarrier()) { + if (unboxedType == JSVAL_TYPE_OBJECT) + masm.callPreBarrier(address, MIRType::Object); + else if (unboxedType == JSVAL_TYPE_STRING) + masm.callPreBarrier(address, MIRType::String); + else + MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType)); + } + + masm.storeUnboxedProperty(address, unboxedType, value, failures); + + attacher.jumpRejoin(masm); + + masm.bind(failures); + attacher.jumpNextStub(masm); +} + +static bool +CanAttachSetUnboxed(JSContext* cx, HandleObject obj, HandleId id, const ConstantOrRegister& val, + bool needsTypeBarrier, bool* checkTypeset, + uint32_t* unboxedOffset, JSValueType* unboxedType) +{ + if (!obj->is<UnboxedPlainObject>()) + return false; + + const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id); + if (property) { + *checkTypeset = false; + if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset)) + return false; + *unboxedOffset = property->offset; + *unboxedType = property->type; + return true; + } + + return false; +} + +static bool +CanAttachSetUnboxedExpando(JSContext* cx, HandleObject obj, HandleId id, + const ConstantOrRegister& val, + bool needsTypeBarrier, bool* checkTypeset, Shape** pshape) +{ + if (!obj->is<UnboxedPlainObject>()) + return false; + + Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando()); + if (!expando) + return false; + + Shape* shape = expando->lookupPure(id); + if (!shape || !shape->hasDefaultSetter() || !shape->hasSlot() || !shape->writable()) + return false; + + *checkTypeset = false; + if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset)) + return false; + + *pshape = shape; + return true; +} + +static bool +CanAttachAddUnboxedExpando(JSContext* cx, HandleObject obj, HandleShape oldShape, + HandleId id, const ConstantOrRegister& val, + bool needsTypeBarrier, bool* checkTypeset) +{ + if (!obj->is<UnboxedPlainObject>()) + return false; + + Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando()); + if (!expando || expando->inDictionaryMode()) + return false; + + Shape* newShape = expando->lastProperty(); + if (newShape->isEmptyShape() || newShape->propid() != id || newShape->previous() != oldShape) + return false; + + MOZ_ASSERT(newShape->hasDefaultSetter() && newShape->hasSlot() && newShape->writable()); + + if (PrototypeChainShadowsPropertyAdd(cx, obj, id)) + return false; + + *checkTypeset = false; + if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset)) + return false; + + return true; +} + +bool +SetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, bool* emitted) +{ + MOZ_ASSERT(!*emitted); + + bool checkTypeset = false; + uint32_t unboxedOffset; + JSValueType unboxedType; + if (!CanAttachSetUnboxed(cx, obj, id, value(), needsTypeBarrier(), &checkTypeset, + &unboxedOffset, &unboxedType)) + { + return true; + } + + *emitted = true; + + MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); + StubAttacher attacher(*this); + + Label failures; + emitIdGuard(masm, id, &failures); + + GenerateSetUnboxed(cx, masm, attacher, obj, id, unboxedOffset, unboxedType, + object(), temp(), value(), checkTypeset, &failures); + return linkAndAttachStub(cx, masm, attacher, ion, "set_unboxed", + JS::TrackedOutcome::ICSetPropStub_SetUnboxed); +} + bool SetPropertyIC::tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted) @@ -3225,6 +3514,26 @@ SetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip } bool +SetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, bool* emitted) +{ + MOZ_ASSERT(!*emitted); + + RootedShape shape(cx); + bool checkTypeset = false; + if (!CanAttachSetUnboxedExpando(cx, obj, id, value(), needsTypeBarrier(), + &checkTypeset, shape.address())) + { + return true; + } + + if (!attachSetSlot(cx, outerScript, ion, obj, shape, checkTypeset)) + return false; + *emitted = true; + return true; +} + +bool SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleValue idval, HandleValue value, MutableHandleId id, bool* emitted, bool* tryNativeAddSlot) @@ -3249,6 +3558,12 @@ SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, emitted, tryNativeAddSlot)) return false; + + if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, emitted)) + return false; + + if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, emitted)) + return false; } if (idval.isInt32()) { @@ -3300,6 +3615,16 @@ SetPropertyIC::tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScri return true; } + checkTypeset = false; + if (CanAttachAddUnboxedExpando(cx, obj, oldShape, id, value(), needsTypeBarrier(), + &checkTypeset)) + { + if (!attachAddSlot(cx, outerScript, ion, obj, id, oldShape, oldGroup, checkTypeset)) + return false; + *emitted = true; + return true; + } + return true; } @@ -3321,6 +3646,11 @@ SetPropertyIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex return false; oldShape = obj->maybeShape(); + if (obj->is<UnboxedPlainObject>()) { + MOZ_ASSERT(!oldShape); + if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) + oldShape = expando->lastProperty(); + } } RootedId id(cx); |