diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 9d7f47ac5025..e166b680bbdc 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -1225,7 +1225,7 @@ GetPropertyIC::tryAttachNative(JSContext *cx, IonScript *ion, HandleObject obj, switch (type) { case CanAttachReadSlot: GenerateReadSlot(cx, ion, masm, attacher, obj, holder, - shape, object(), output()); + shape, object(), output()); attachKind = idempotent() ? "idempotent reading" : "non idempotent reading"; break; @@ -1937,83 +1937,89 @@ IonCache::destroy() { } -bool -SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, - HandleShape shape, bool checkTypeset) +static void +GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, + JSObject *obj, Shape *shape, Register object, ConstantOrRegister value, + bool needsTypeBarrier, bool checkTypeset) { JS_ASSERT(obj->isNative()); - MacroAssembler masm(cx); - RepatchStubAppender attacher(*this); - Label failures, barrierFailure; masm.branchPtr(Assembler::NotEqual, - Address(object(), JSObject::offsetOfShape()), + Address(object, JSObject::offsetOfShape()), ImmGCPtr(obj->lastProperty()), &failures); // Guard that the incoming value is in the type set for the property // if a type barrier is required. - if (needsTypeBarrier()) { + if (needsTypeBarrier) { // We can't do anything that would change the HeapTypeSet, so // just guard that it's already there. // Obtain and guard on the TypeObject of the object. - types::TypeObject *type = obj->getType(cx); + types::TypeObject *type = obj->type(); masm.branchPtr(Assembler::NotEqual, - Address(object(), JSObject::offsetOfType()), + Address(object, JSObject::offsetOfType()), ImmGCPtr(type), &failures); if (checkTypeset) { - TypedOrValueRegister valReg = value().reg(); - types::HeapTypeSet *propTypes = type->maybeGetProperty(NameToId(name())); + TypedOrValueRegister valReg = value.reg(); + types::HeapTypeSet *propTypes = type->maybeGetProperty(shape->propid()); JS_ASSERT(propTypes); JS_ASSERT(!propTypes->unknown()); - Register scratchReg = object(); + Register scratchReg = object; masm.push(scratchReg); masm.guardTypeSet(valReg, propTypes, scratchReg, &barrierFailure); - masm.pop(object()); + masm.pop(object); } } if (obj->isFixedSlot(shape->slot())) { - Address addr(object(), JSObject::getFixedSlotOffset(shape->slot())); + Address addr(object, JSObject::getFixedSlotOffset(shape->slot())); if (cx->zone()->needsBarrier()) masm.callPreBarrier(addr, MIRType_Value); - masm.storeConstantOrRegister(value(), addr); + masm.storeConstantOrRegister(value, addr); } else { - Register slotsReg = object(); - masm.loadPtr(Address(object(), JSObject::offsetOfSlots()), slotsReg); + Register slotsReg = object; + masm.loadPtr(Address(object, JSObject::offsetOfSlots()), slotsReg); Address addr(slotsReg, obj->dynamicSlotIndex(shape->slot()) * sizeof(Value)); if (cx->zone()->needsBarrier()) masm.callPreBarrier(addr, MIRType_Value); - masm.storeConstantOrRegister(value(), addr); + masm.storeConstantOrRegister(value, addr); } attacher.jumpRejoin(masm); if (barrierFailure.used()) { masm.bind(&barrierFailure); - masm.pop(object()); + masm.pop(object); } masm.bind(&failures); attacher.jumpNextStub(masm); +} +bool +SetPropertyIC::attachSetSlot(JSContext *cx, IonScript *ion, HandleObject obj, + HandleShape shape, bool checkTypeset) +{ + MacroAssembler masm(cx); + RepatchStubAppender attacher(*this); + GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(), + checkTypeset); return linkAndAttachStub(cx, masm, attacher, ion, "setting"); } static bool IsCacheableSetPropCallNative(HandleObject obj, HandleObject holder, HandleShape shape) { - if (!obj->isNative()) - return false; + JS_ASSERT(obj->isNative()); if (!shape || !IsCacheableProtoChain(obj, holder)) return false; @@ -2024,11 +2030,9 @@ IsCacheableSetPropCallNative(HandleObject obj, HandleObject holder, HandleShape } static bool -IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, - HandleShape shape) +IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, HandleShape shape) { - if (!obj->isNative()) - return false; + JS_ASSERT(obj->isNative()); if (!shape) return false; @@ -2483,7 +2487,7 @@ SetPropertyIC::attachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObj } bool -SetPropertyIC::attachSetterCall(JSContext *cx, IonScript *ion, +SetPropertyIC::attachCallSetter(JSContext *cx, IonScript *ion, HandleObject obj, HandleObject holder, HandleShape shape, void *returnAddr) { @@ -2518,30 +2522,26 @@ SetPropertyIC::attachSetterCall(JSContext *cx, IonScript *ion, return linkAndAttachStub(cx, masm, attacher, ion, "setter call"); } -bool -SetPropertyIC::attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, - HandleShape oldShape, HandleShape newShape, - HandleShape propShape) +static void +GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, + JSObject *obj, Shape *oldShape, Register object, ConstantOrRegister value) { JS_ASSERT(obj->isNative()); - MacroAssembler masm(cx); - RepatchStubAppender attacher(*this); - Label failures; - /* Guard the type of the object */ - masm.branchPtr(Assembler::NotEqual, Address(object(), JSObject::offsetOfType()), + // Guard the type of the object + masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfType()), ImmGCPtr(obj->type()), &failures); - /* Guard shapes along prototype chain. */ - masm.branchTestObjShape(Assembler::NotEqual, object(), oldShape, &failures); + // Guard shapes along prototype chain. + masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, &failures); Label protoFailures; - masm.push(object()); // save object reg because we clobber it + masm.push(object); // save object reg because we clobber it JSObject *proto = obj->getProto(); - Register protoReg = object(); + Register protoReg = object; while (proto) { Shape *protoShape = proto->lastProperty(); @@ -2555,48 +2555,58 @@ SetPropertyIC::attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, proto = proto->getProto(); } - masm.pop(object()); // restore object reg + masm.pop(object); // restore object reg - /* Changing object shape. Write the object's new shape. */ - Address shapeAddr(object(), JSObject::offsetOfShape()); + // Changing object shape. Write the object's new shape. + Shape *newShape = obj->lastProperty(); + Address shapeAddr(object, JSObject::offsetOfShape()); if (cx->zone()->needsBarrier()) masm.callPreBarrier(shapeAddr, MIRType_Shape); masm.storePtr(ImmGCPtr(newShape), shapeAddr); - /* Set the value on the object. */ - if (obj->isFixedSlot(propShape->slot())) { - Address addr(object(), JSObject::getFixedSlotOffset(propShape->slot())); - masm.storeConstantOrRegister(value(), addr); + // Set the value on the object. Since this is an add, obj->lastProperty() + // must be the shape of the property we are adding. + if (obj->isFixedSlot(newShape->slot())) { + Address addr(object, JSObject::getFixedSlotOffset(newShape->slot())); + masm.storeConstantOrRegister(value, addr); } else { - Register slotsReg = object(); + Register slotsReg = object; - masm.loadPtr(Address(object(), JSObject::offsetOfSlots()), slotsReg); + masm.loadPtr(Address(object, JSObject::offsetOfSlots()), slotsReg); - Address addr(slotsReg, obj->dynamicSlotIndex(propShape->slot()) * sizeof(Value)); - masm.storeConstantOrRegister(value(), addr); + Address addr(slotsReg, obj->dynamicSlotIndex(newShape->slot()) * sizeof(Value)); + masm.storeConstantOrRegister(value, addr); } - /* Success. */ + // Success. attacher.jumpRejoin(masm); - /* Failure. */ + // Failure. masm.bind(&protoFailures); - masm.pop(object()); + masm.pop(object); masm.bind(&failures); attacher.jumpNextStub(masm); +} +bool +SetPropertyIC::attachAddSlot(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldShape) +{ + MacroAssembler masm(cx); + RepatchStubAppender attacher(*this); + GenerateAddSlot(cx, masm, attacher, obj, oldShape, object(), value()); return linkAndAttachStub(cx, masm, attacher, ion, "adding"); } static bool -IsPropertySetInlineable(JSContext *cx, const SetPropertyIC &cache, HandleObject obj, - HandleId id, MutableHandleShape pshape, bool *checkTypeset) +IsPropertySetInlineable(HandleObject obj, HandleId id, MutableHandleShape pshape, + ConstantOrRegister val, bool needsTypeBarrier, bool *checkTypeset) { - if (!obj->isNative()) - return false; + JS_ASSERT(obj->isNative()); - pshape.set(obj->nativeLookup(cx, id)); + // Do a pure non-proto chain climbing lookup. See note in + // CanAttachNativeGetProp. + pshape.set(obj->nativeLookupPure(id)); if (!pshape) return false; @@ -2611,17 +2621,16 @@ IsPropertySetInlineable(JSContext *cx, const SetPropertyIC &cache, HandleObject return false; bool shouldCheck = false; - types::TypeObject *type = obj->getType(cx); - if (cache.needsTypeBarrier() && !type->unknownProperties()) { + types::TypeObject *type = obj->type(); + if (needsTypeBarrier && !type->unknownProperties()) { types::HeapTypeSet *propTypes = type->maybeGetProperty(id); if (!propTypes) return false; if (!propTypes->unknown()) { shouldCheck = true; - ConstantOrRegister val = cache.value(); if (val.constant()) { // If the input is a constant, then don't bother if the barrier will always fail. - if (!propTypes->hasType(types::GetValueType(cache.value().value()))) + if (!propTypes->hasType(types::GetValueType(val.value()))) return false; shouldCheck = false; } else { @@ -2646,20 +2655,22 @@ IsPropertySetInlineable(JSContext *cx, const SetPropertyIC &cache, HandleObject } static bool -IsPropertyAddInlineable(JSContext *cx, HandleObject obj, HandleId id, uint32_t oldSlots, - MutableHandleShape pShape) +IsPropertyAddInlineable(HandleObject obj, HandleId id, uint32_t oldSlots, HandleShape oldShape) { - if (!obj->isNative()) + JS_ASSERT(obj->isNative()); + + // If the shape of the object did not change, then this was not an add. + if (obj->lastProperty() == oldShape) return false; - // This is not a Add, the property exists. - if (pShape.get()) - return false; - - RootedShape shape(cx, obj->nativeLookup(cx, id)); + Shape *shape = obj->nativeLookupPure(id); if (!shape || shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter()) return false; + // If we have a shape at this point and the object's shape changed, then + // the shape must be the one we just added. + JS_ASSERT(shape == obj->lastProperty()); + // If object has a non-default resolve hook, don't inline if (obj->getClass()->resolve != JS_ResolveStub) return false; @@ -2672,21 +2683,22 @@ IsPropertyAddInlineable(JSContext *cx, HandleObject obj, HandleId id, uint32_t o if (!obj->nonProxyIsExtensible() || !shape->writable()) return false; - // walk up the object prototype chain and ensure that all prototypes + // Walk up the object prototype chain and ensure that all prototypes // are native, and that all prototypes have no getter or setter // defined on the property for (JSObject *proto = obj->getProto(); proto; proto = proto->getProto()) { - // if prototype is non-native, don't optimize + // If prototype is non-native, don't optimize if (!proto->isNative()) return false; - // if prototype defines this property in a non-plain way, don't optimize - Shape *protoShape = proto->nativeLookup(cx, id); + // If prototype defines this property in a non-plain way, don't optimize + Shape *protoShape = proto->nativeLookupPure(id); if (protoShape && !protoShape->hasDefaultSetter()) return false; - // Otherise, if there's no such property, watch out for a resolve hook that would need - // to be invoked and thus prevent inlining of property addition. + // Otherwise, if there's no such property, watch out for a resolve + // hook that would need to be invoked and thus prevent inlining of + // property addition. if (proto->getClass()->resolve != JS_ResolveStub) return false; } @@ -2697,10 +2709,40 @@ IsPropertyAddInlineable(JSContext *cx, HandleObject obj, HandleId id, uint32_t o if (obj->numDynamicSlots() != oldSlots) return false; - pShape.set(shape); return true; } +static SetPropertyIC::NativeSetPropCacheability +CanAttachNativeSetProp(HandleObject obj, HandleId id, ConstantOrRegister val, + bool needsTypeBarrier, MutableHandleObject holder, + MutableHandleShape shape, bool *checkTypeset) +{ + if (!obj->isNative()) + return SetPropertyIC::CanAttachNone; + + // See if the property exists on the object. + if (IsPropertySetInlineable(obj, id, shape, val, needsTypeBarrier, checkTypeset)) + return SetPropertyIC::CanAttachSetSlot; + + // If we couldn't find the property on the object itself, do a full, but + // still pure lookup for setters. + if (!LookupPropertyPure(obj, id, holder.address(), shape.address())) + return SetPropertyIC::CanAttachNone; + + // If the object doesn't have the property, we don't know if we can attach + // a stub to add the property until we do the VM call to add. + if (!shape) + return SetPropertyIC::MaybeCanAttachAddSlot; + + if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) || + IsCacheableSetPropCallNative(obj, holder, shape)) + { + return SetPropertyIC::CanAttachCallSetter; + } + + return SetPropertyIC::CanAttachNone; +} + bool SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value) @@ -2713,12 +2755,11 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, SetPropertyIC &cache = ion->getCache(cacheIndex).toSetProperty(); RootedPropertyName name(cx, cache.name()); RootedId id(cx, AtomToId(name)); - RootedShape shape(cx); - RootedObject holder(cx); // Stop generating new stubs once we hit the stub count limit, see // GetPropertyCache. bool inlinable = cache.canAttachStub() && !obj->watched(); + NativeSetPropCacheability canCache = CanAttachNone; bool addedSetterStub = false; if (inlinable) { if (!addedSetterStub && obj->is()) { @@ -2746,24 +2787,29 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, addedSetterStub = true; } } + + // Make sure the object de-lazifies its type. We do this here so that + // the parallel IC can share code that assumes that native objects all + // have a type object. + if (obj->isNative() && !obj->getType(cx)) + return false; + RootedShape shape(cx); + RootedObject holder(cx); bool checkTypeset; - if (!addedSetterStub && IsPropertySetInlineable(cx, cache, obj, id, &shape, &checkTypeset)) { - if (!cache.attachNativeExisting(cx, ion, obj, shape, checkTypeset)) + canCache = CanAttachNativeSetProp(obj, id, cache.value(), cache.needsTypeBarrier(), + &holder, &shape, &checkTypeset); + + if (!addedSetterStub && canCache == CanAttachSetSlot) { + if (!cache.attachSetSlot(cx, ion, obj, shape, checkTypeset)) return false; addedSetterStub = true; - } else if (!addedSetterStub) { - RootedObject holder(cx); - if (!JSObject::lookupProperty(cx, obj, name, &holder, &shape)) - return false; + } - if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) || - IsCacheableSetPropCallNative(obj, holder, shape)) - { - if (!cache.attachSetterCall(cx, ion, obj, holder, shape, returnAddr)) - return false; - addedSetterStub = true; - } + if (!addedSetterStub && canCache == CanAttachCallSetter) { + if (!cache.attachCallSetter(cx, ion, obj, holder, shape, returnAddr)) + return false; + addedSetterStub = true; } } @@ -2775,12 +2821,11 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, return false; // The property did not exist before, now we can try to inline the property add. - if (inlinable && !addedSetterStub && !cache.needsTypeBarrier() && - obj->lastProperty() != oldShape && - IsPropertyAddInlineable(cx, obj, id, oldSlots, &shape)) + if (!addedSetterStub && canCache == MaybeCanAttachAddSlot && + !cache.needsTypeBarrier() && + IsPropertyAddInlineable(obj, id, oldSlots, oldShape)) { - RootedShape newShape(cx, obj->lastProperty()); - if (!cache.attachNativeAdding(cx, ion, obj, oldShape, newShape, shape)) + if (!cache.attachAddSlot(cx, ion, obj, oldShape)) return false; } diff --git a/js/src/jit/IonCaches.h b/js/src/jit/IonCaches.h index ccc7f75f010a..bb984db72c12 100644 --- a/js/src/jit/IonCaches.h +++ b/js/src/jit/IonCaches.h @@ -678,12 +678,18 @@ class SetPropertyIC : public RepatchIonCache return hasGenericProxyStub_; } - bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, - HandleShape shape, bool checkTypeset); - bool attachSetterCall(JSContext *cx, IonScript *ion, HandleObject obj, + enum NativeSetPropCacheability { + CanAttachNone, + CanAttachSetSlot, + MaybeCanAttachAddSlot, + CanAttachCallSetter + }; + + bool attachSetSlot(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape, + bool checkTypeset); + bool attachCallSetter(JSContext *cx, IonScript *ion, HandleObject obj, HandleObject holder, HandleShape shape, void *returnAddr); - bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldshape, - HandleShape newshape, HandleShape propshape); + bool attachAddSlot(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldShape); bool attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAddr); bool attachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObject obj, void *returnAddr); @@ -772,7 +778,7 @@ class GetElementIC : public RepatchIonCache static bool update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, - MutableHandleValue vp); + MutableHandleValue vp); void incFailedUpdates() { failedUpdates_++;