diff --git a/js/src/jit-test/tests/ion/bug911708.js b/js/src/jit-test/tests/ion/bug911708.js new file mode 100644 index 00000000000..1f3b575c26d --- /dev/null +++ b/js/src/jit-test/tests/ion/bug911708.js @@ -0,0 +1,8 @@ +function x() { + yield x +} +new(x) +ParallelArray([7247], function() { + --x + eval("") +}) diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index c950f7147b6..aa35684cf49 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -1946,8 +1946,8 @@ IonCache::destroy() } bool -SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion, - HandleObject obj, HandleShape shape) +SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, + HandleShape shape, bool checkTypeset) { JS_ASSERT(obj->isNative()); @@ -1961,7 +1961,7 @@ SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion, // Guard that the incoming value is in the type set for the property // if a type barrier is required. - if (needsTypeBarrier() && !value().constant()) { + if (needsTypeBarrier()) { // We can't do anything that would change the HeapTypeSet, so // just guard that it's already there. @@ -1971,29 +1971,28 @@ SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion, Address(object(), JSObject::offsetOfType()), ImmGCPtr(type), &failures); - if (!type->unknownProperties()) { + if (checkTypeset) { TypedOrValueRegister valReg = value().reg(); RootedId id(cx, types::IdToTypeId(AtomToId(name()))); types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, id); JS_ASSERT(propTypes); + JS_ASSERT(!propTypes->unknown()); - if (!propTypes->unknown()) { - Label barrierSuccess; - Label barrierFailure; + Label barrierSuccess; + Label barrierFailure; - Register scratchReg = object(); - masm.push(scratchReg); + Register scratchReg = object(); + masm.push(scratchReg); - masm.guardTypeSet(valReg, propTypes, scratchReg, - &barrierSuccess, &barrierFailure); + masm.guardTypeSet(valReg, propTypes, scratchReg, + &barrierSuccess, &barrierFailure); - masm.bind(&barrierFailure); - masm.pop(object()); - masm.jump(&failures); + masm.bind(&barrierFailure); + masm.pop(object()); + masm.jump(&failures); - masm.bind(&barrierSuccess); - masm.pop(object()); - } + masm.bind(&barrierSuccess); + masm.pop(object()); } } @@ -2605,7 +2604,7 @@ SetPropertyIC::attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, static bool IsPropertySetInlineable(JSContext *cx, const SetPropertyIC &cache, HandleObject obj, - HandleId id, MutableHandleShape pshape) + HandleId id, MutableHandleShape pshape, bool *checkTypeset) { if (!obj->isNative()) return false; @@ -2624,20 +2623,39 @@ IsPropertySetInlineable(JSContext *cx, const SetPropertyIC &cache, HandleObject if (!shape->writable()) return false; + bool shouldCheck = false; types::TypeObject *type = obj->getType(cx); if (cache.needsTypeBarrier() && !type->unknownProperties()) { RootedId typeId(cx, types::IdToTypeId(id)); types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, typeId); if (!propTypes) return false; - if (cache.value().constant() && !propTypes->unknown()) { - // If the input is a constant, then don't bother if the barrier will always fail. - if (!propTypes->hasType(types::GetValueType(cache.value().value()))) - 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()))) + return false; + shouldCheck = false; + } else { + TypedOrValueRegister reg = val.reg(); + // We can do the same trick as above for primitive types of specialized registers. + // TIs handling of objects is complicated enough to warrant a runtime + // check, as we can't statically handle the case where the typeset + // contains the specific object, but doesn't have ANYOBJECT set. + if (reg.hasTyped() && reg.type() != MIRType_Object) { + JSValueType valType = ValueTypeFromMIRType(reg.type()); + if (!propTypes->hasType(types::Type::PrimitiveType(valType))) + return false; + shouldCheck = false; + } + } } } pshape.set(shape); + *checkTypeset = shouldCheck; return true; } @@ -2744,8 +2762,9 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, } } RootedShape shape(cx); - if (!addedSetterStub && IsPropertySetInlineable(cx, cache, obj, id, &shape)) { - if (!cache.attachNativeExisting(cx, ion, obj, shape)) + bool checkTypeset; + if (!addedSetterStub && IsPropertySetInlineable(cx, cache, obj, id, &shape, &checkTypeset)) { + if (!cache.attachNativeExisting(cx, ion, obj, shape, checkTypeset)) return false; addedSetterStub = true; } else if (!addedSetterStub) { diff --git a/js/src/jit/IonCaches.h b/js/src/jit/IonCaches.h index 885f7fc0214..b13cf785c56 100644 --- a/js/src/jit/IonCaches.h +++ b/js/src/jit/IonCaches.h @@ -687,7 +687,8 @@ class SetPropertyIC : public RepatchIonCache return hasGenericProxyStub_; } - bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape); + bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, + HandleShape shape, bool checkTypeset); bool attachSetterCall(JSContext *cx, IonScript *ion, HandleObject obj, HandleObject holder, HandleShape shape, void *returnAddr); bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldshape, diff --git a/js/src/jit/IonMacroAssembler.h b/js/src/jit/IonMacroAssembler.h index 857ff2aff17..f40b8c96fb9 100644 --- a/js/src/jit/IonMacroAssembler.h +++ b/js/src/jit/IonMacroAssembler.h @@ -620,6 +620,7 @@ class MacroAssembler : public MacroAssemblerSpecific Register extractObject(const TypedOrValueRegister ®, Register scratch) { if (reg.hasValue()) return extractObject(reg.valueReg(), scratch); + JS_ASSERT(reg.type() == MIRType_Object); return reg.typedReg().gpr(); }