Bug 930048 - Remove need to read objects directly when optimizing singleton accesses, r=jandem.

This commit is contained in:
Brian Hackett 2013-10-29 16:10:59 -06:00
parent 4c8f0feb82
commit 98ec75f9e6
15 changed files with 212 additions and 247 deletions

View File

@ -741,7 +741,7 @@ TryPreserveWrapper(JSObject* obj)
// Can only be called with the immediate prototype of the instance object. Can
// only be called on the prototype of an object known to be a DOM instance.
bool
InstanceClassHasProtoAtDepth(JS::Handle<JSObject*> protoObject, uint32_t protoID,
InstanceClassHasProtoAtDepth(JSObject* protoObject, uint32_t protoID,
uint32_t depth)
{
const DOMClass* domClass = static_cast<const DOMClass*>(

View File

@ -969,7 +969,7 @@ TryPreserveWrapper(JSObject* obj);
// Can only be called with the immediate prototype of the instance object. Can
// only be called on the prototype of an object known to be a DOM instance.
bool
InstanceClassHasProtoAtDepth(JS::Handle<JSObject*> protoObject, uint32_t protoID,
InstanceClassHasProtoAtDepth(JSObject* protoObject, uint32_t protoID,
uint32_t depth);
// Only set allowNativeWrapper to false if you really know you need it, if in

View File

@ -1957,10 +1957,10 @@ AnalyzePoppedThis(JSContext *cx, types::TypeObject *type,
return true;
}
if (baseobj->slotSpan() >= (types::TYPE_FLAG_DEFINITE_MASK >> types::TYPE_FLAG_DEFINITE_SHIFT)) {
// Maximum number of definite properties added.
// Don't add definite properties to an object which won't fit in its
// fixed slots.
if (GetGCKindSlots(gc::GetGCObjectKind(baseobj->slotSpan() + 1)) <= baseobj->slotSpan())
return true;
}
// Assignments to new properties must always execute.
if (!definitelyExecuted)

View File

@ -5028,17 +5028,18 @@ IonBuilder::makeCallsiteClone(JSFunction *target, MDefinition *fun)
return clone;
}
static bool
TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, JSFunction *func,
JSJitInfo::OpType opType)
bool
IonBuilder::testShouldDOMCall(types::TypeSet *inTypes,
JSFunction *func, JSJitInfo::OpType opType)
{
if (!func->isNative() || !func->jitInfo())
return false;
// If all the DOM objects flowing through are legal with this
// property, we can bake in a call to the bottom half of the DOM
// accessor
DOMInstanceClassMatchesProto instanceChecker =
GetDOMCallbacks(cx->runtime())->instanceClassMatchesProto;
GetDOMCallbacks(compartment->runtimeFromAnyThread())->instanceClassMatchesProto;
const JSJitInfo *jinfo = func->jitInfo();
if (jinfo->type != opType)
@ -5049,8 +5050,8 @@ TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, JSFunction *func,
if (!curType)
continue;
RootedObject protoRoot(cx, curType->proto().toObjectOrNull());
if (!instanceChecker(protoRoot, jinfo->protoID, jinfo->depth))
JSObject *proto = curType->proto().toObjectOrNull();
if (!instanceChecker(proto, jinfo->protoID, jinfo->depth))
return false;
}
@ -5058,7 +5059,7 @@ TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, JSFunction *func,
}
static bool
TestAreKnownDOMTypes(JSContext *cx, types::TypeSet *inTypes)
TestAreKnownDOMTypes(types::TypeSet *inTypes)
{
if (inTypes->unknownObject())
return false;
@ -5102,7 +5103,7 @@ ArgumentTypesMatch(MDefinition *def, types::StackTypeSet *calleeTypes)
}
bool
IonBuilder::testNeedsArgumentCheck(JSContext *cx, JSFunction *target, CallInfo &callInfo)
IonBuilder::testNeedsArgumentCheck(JSFunction *target, CallInfo &callInfo)
{
// If we have a known target, check if the caller arg types are a subset of callee.
// Since typeset accumulates and can't decrease that means we don't need to check
@ -5210,14 +5211,14 @@ IonBuilder::makeCallHelper(JSFunction *target, CallInfo &callInfo, bool cloneAtC
// MCall accordingly.
types::TemporaryTypeSet *thisTypes = thisArg->resultTypeSet();
if (thisTypes &&
TestAreKnownDOMTypes(cx, thisTypes) &&
TestShouldDOMCall(cx, thisTypes, target, JSJitInfo::Method))
TestAreKnownDOMTypes(thisTypes) &&
testShouldDOMCall(thisTypes, target, JSJitInfo::Method))
{
call->setDOMFunction();
}
}
if (target && !testNeedsArgumentCheck(cx, target, callInfo))
if (target && !testNeedsArgumentCheck(target, callInfo))
call->disableArgCheck();
call->initFunction(callInfo.fun());
@ -5323,10 +5324,7 @@ IonBuilder::jsop_eval(uint32_t argc)
{
JSString *str = string->getOperand(1)->toConstant()->value().toString();
bool match;
if (!JS_StringEqualsAscii(cx, str, "()", &match))
return false;
if (match) {
if (str->isLinear() && StringEqualsAscii(&str->asLinear(), "()")) {
MDefinition *name = string->getOperand(0);
MInstruction *dynamicName = MGetDynamicName::New(scopeChain, name);
current->add(dynamicName);
@ -5520,20 +5518,11 @@ IonBuilder::jsop_initprop(PropertyName *name)
MDefinition *value = current->pop();
MDefinition *obj = current->peek(-1);
RootedObject templateObject(cx, obj->toNewObject()->templateObject());
JSObject *templateObject = obj->toNewObject()->templateObject();
if (!CanEffectlesslyCallLookupGenericOnObject(cx, templateObject, name))
return abort("INITPROP template object is special");
Shape *shape = templateObject->nativeLookupPure(name);
RootedObject holder(cx);
RootedShape shape(cx);
RootedId id(cx, NameToId(name));
bool res = LookupPropertyWithFlags(cx, templateObject, id,
0, &holder, &shape);
if (!res)
return false;
if (!shape || holder != templateObject) {
if (!shape) {
// JSOP_NEWINIT becomes an MNewObject without preconfigured properties.
MInitProp *init = MInitProp::New(obj, name, value);
current->add(init);
@ -5554,7 +5543,7 @@ IonBuilder::jsop_initprop(PropertyName *name)
bool needsBarrier = true;
if (obj->resultTypeSet() &&
!obj->resultTypeSet()->propertyNeedsBarrier(constraints(), id))
!obj->resultTypeSet()->propertyNeedsBarrier(constraints(), NameToId(name)))
{
needsBarrier = false;
}
@ -5962,8 +5951,7 @@ IonBuilder::maybeInsertResume()
}
bool
IonBuilder::testSingletonProperty(JSObject *obj, JSObject *singleton,
PropertyName *name, bool *isKnownConstant)
IonBuilder::testSingletonProperty(JSObject *obj, JSObject *singleton, PropertyName *name)
{
// We would like to completely no-op property/global accesses which can
// produce only a particular JSObject. When indicating the access result is
@ -5973,85 +5961,59 @@ IonBuilder::testSingletonProperty(JSObject *obj, JSObject *singleton,
// the pushed types updated, even if there is no type barrier).
//
// If the access definitely goes through obj, either directly or on the
// prototype chain, then if obj has a defined property now, and the
// property has a default or method shape, then the property is not missing
// and the only way it can become missing in the future is if it is deleted.
// Deletion causes type properties to be explicitly marked with undefined.
// prototype chain, and the object has singleton type, then the type
// information for that property reflects the value that will definitely be
// read on accesses to the object. If the property is later deleted or
// reconfigured as a getter/setter then the type information for the
// property will change and trigger invalidation.
*isKnownConstant = false;
while (obj) {
const Class *clasp = obj->getClass();
if (!clasp->isNative() || clasp->ops.lookupGeneric)
return false;
if (!CanEffectlesslyCallLookupGenericOnObject(cx, obj, name))
return true;
RootedObject objRoot(cx, obj);
RootedId idRoot(cx, NameToId(name));
RootedObject holder(cx);
RootedShape shape(cx);
if (!JSObject::lookupGeneric(cx, objRoot, idRoot, &holder, &shape))
return false;
if (!shape)
return true;
if (!shape->hasDefaultGetter())
return true;
if (!shape->hasSlot())
return true;
if (holder->getSlot(shape->slot()).isUndefined())
return true;
// Ensure the property does not appear anywhere on the prototype chain
// before |holder|, and that |holder| only has the result object for its
// property.
while (true) {
types::TypeObjectKey *objType = types::TypeObjectKey::get(obj);
if (objType->unknownProperties())
return true;
return false;
types::HeapTypeSetKey property = objType->property(NameToId(name), context());
if (obj != holder) {
if (property.notEmpty(constraints()))
return true;
} else {
if (property.singleton(constraints()) != singleton)
return true;
break;
if (property.isOwnProperty(constraints())) {
if (obj->hasSingletonType())
return property.singleton(constraints()) == singleton;
return false;
}
if (clasp->resolve != JS_ResolveStub && clasp->resolve != (JSResolveOp)fun_resolve)
return false;
obj = obj->getProto();
}
*isKnownConstant = true;
return true;
return false;
}
bool
IonBuilder::testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton,
JSObject *globalObj, PropertyName *name,
bool *isKnownConstant, bool *testObject,
bool *testString)
IonBuilder::testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton, PropertyName *name,
bool *testObject, bool *testString)
{
// As for TestSingletonProperty, but the input is any value in a type set
// rather than a specific object. If testObject is set then the constant
// result can only be used after ensuring the input is an object.
*isKnownConstant = false;
*testObject = false;
*testString = false;
types::TemporaryTypeSet *types = obj->resultTypeSet();
if (!types && obj->type() != MIRType_String)
return true;
return false;
if (types && types->unknownObject())
return true;
return false;
JSObject *objectSingleton = types ? types->getSingleton() : nullptr;
if (objectSingleton)
return testSingletonProperty(objectSingleton, singleton, name, isKnownConstant);
if (!globalObj)
return true;
return testSingletonProperty(objectSingleton, singleton, name);
JSProtoKey key;
switch (obj->type()) {
@ -6077,7 +6039,7 @@ IonBuilder::testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton,
}
if (!types->maybeObject())
return true;
return false;
// For property accesses which may be on many objects, we just need to
// find a prototype common to all the objects; if that prototype
@ -6088,37 +6050,33 @@ IonBuilder::testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton,
continue;
if (object->unknownProperties())
return true;
return false;
types::HeapTypeSetKey property = object->property(NameToId(name), context());
if (property.notEmpty(constraints()))
return true;
if (property.isOwnProperty(constraints()))
return false;
if (JSObject *proto = object->proto().toObjectOrNull()) {
// Test this type.
bool thoughtConstant = false;
if (!testSingletonProperty(proto, singleton, name, &thoughtConstant))
if (!testSingletonProperty(proto, singleton, name))
return false;
if (!thoughtConstant)
return true;
} else {
// Can't be on the prototype chain with no prototypes...
return true;
return false;
}
}
// If this is not a known object, a test will be needed.
*testObject = (obj->type() != MIRType_Object);
*isKnownConstant = true;
return true;
}
default:
return true;
}
RootedObject proto(cx);
if (!js_GetClassPrototype(cx, key, &proto, nullptr))
return false;
JSObject *proto = GetClassPrototypePure(&script()->global(), key);
if (proto)
return testSingletonProperty(proto, singleton, name);
return testSingletonProperty(proto, singleton, name, isKnownConstant);
return false;
}
// Given an observed type set, annotates the IR as much as possible:
@ -6196,12 +6154,24 @@ IonBuilder::pushTypeBarrier(MInstruction *ins, types::TemporaryTypeSet *observed
return true;
}
static size_t
NumFixedSlots(JSObject *object)
{
// Note: we can't use object->numFixedSlots() here, as this will read the
// shape and can race with the main thread if we are building off thread.
// The allocation kind and object class (which goes through the type) can
// be read freely, however.
gc::AllocKind kind = object->tenuredGetAllocKind();
return gc::GetGCKindSlots(kind, object->getClass());
}
bool
IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psucceeded)
{
jsid id = NameToId(name);
JS_ASSERT(staticObject->is<GlobalObject>() || staticObject->is<CallObject>());
JS_ASSERT(staticObject->hasSingletonType());
*psucceeded = true;
@ -6215,42 +6185,34 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
return pushConstant(compartment->runtimeFromAnyThread()->positiveInfinityValue);
}
// For the fastest path, the property must be found, and it must be found
// as a normal data property on exactly the global object.
Shape *shape = staticObject->nativeLookup(cx, id);
if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) {
types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject);
if (staticType->unknownProperties()) {
*psucceeded = false;
return true;
}
types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject);
Maybe<types::HeapTypeSetKey> propertyTypes;
if (!staticType->unknownProperties()) {
propertyTypes.construct(staticType->property(id, context()));
if (propertyTypes.ref().configured(constraints(), staticType)) {
// The property has been reconfigured as non-configurable, non-enumerable
// or non-writable.
*psucceeded = false;
return true;
}
types::HeapTypeSetKey property = staticType->property(id, context());
if (!property.maybeTypes() ||
!property.maybeTypes()->definiteProperty() ||
property.configured(constraints(), staticType))
{
// The property has been reconfigured as non-configurable, non-enumerable
// or non-writable.
*psucceeded = false;
return true;
}
types::TemporaryTypeSet *types = bytecodeTypes(pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), staticType,
bool barrier = PropertyReadNeedsTypeBarrier(context(), constraints(), staticType,
name, types, /* updateObserved = */ true);
// If the property is permanent, a shape guard isn't necessary.
JSObject *singleton = types->getSingleton();
JSValueType knownType = types->getKnownTypeTag();
if (!barrier) {
if (singleton) {
// Try to inline a known constant value.
bool isKnownConstant;
if (!testSingletonProperty(staticObject, singleton, name, &isKnownConstant))
return false;
if (isKnownConstant)
if (testSingletonProperty(staticObject, singleton, name))
return pushConstant(ObjectValue(*singleton));
}
if (knownType == JSVAL_TYPE_UNDEFINED)
@ -6262,16 +6224,12 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
MInstruction *obj = MConstant::New(ObjectValue(*staticObject));
current->add(obj);
// If we have a property typeset, the HeapTypeSetIsConfigured call will
// trigger recompilation if the property is deleted or reconfigured.
if (propertyTypes.empty() && shape->configurable())
obj = addShapeGuard(obj, staticObject->lastProperty(), Bailout_ShapeGuard);
MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
if (barrier)
rvalType = MIRType_Value;
return loadSlot(obj, shape, rvalType, barrier, types);
return loadSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
rvalType, barrier, types);
}
// Whether 'types' includes all possible values represented by input/inputTypes.
@ -6322,24 +6280,21 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name)
if (staticObject->watched())
return jsop_setprop(name);
// For the fastest path, the property must be found, and it must be found
// as a normal data property on exactly the global object.
Shape *shape = staticObject->nativeLookup(cx, id);
if (!shape || !shape->hasDefaultSetter() || !shape->writable() || !shape->hasSlot())
return jsop_setprop(name);
types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject);
if (staticType->unknownProperties())
return jsop_setprop(name);
types::HeapTypeSetKey propertyTypes = staticType->property(id);
if (propertyTypes.configured(constraints(), staticType)) {
types::HeapTypeSetKey property = staticType->property(id);
if (!property.maybeTypes() ||
!property.maybeTypes()->definiteProperty() ||
property.configured(constraints(), staticType))
{
// The property has been reconfigured as non-configurable, non-enumerable
// or non-writable.
return jsop_setprop(name);
}
if (!TypeSetIncludes(propertyTypes.maybeTypes(), value->type(), value->resultTypeSet()))
if (!TypeSetIncludes(property.maybeTypes(), value->type(), value->resultTypeSet()))
return jsop_setprop(name);
current->pop();
@ -6352,18 +6307,15 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name)
current->add(MPostWriteBarrier::New(obj, value));
// If the property has a known type, we may be able to optimize typed stores by not
// storing the type tag. This only works if the property does not have its initial
// |undefined| value; if |undefined| is assigned at a later point, it will be added
// to the type set.
// storing the type tag.
MIRType slotType = MIRType_None;
if (!staticObject->getSlot(shape->slot()).isUndefined()) {
JSValueType knownType = propertyTypes.knownTypeTag(constraints());
if (knownType != JSVAL_TYPE_UNKNOWN)
slotType = MIRTypeFromValueType(knownType);
}
JSValueType knownType = property.knownTypeTag(constraints());
if (knownType != JSVAL_TYPE_UNKNOWN)
slotType = MIRTypeFromValueType(knownType);
bool needsBarrier = propertyTypes.needsBarrier(constraints());
return storeSlot(obj, shape, value, needsBarrier, slotType);
bool needsBarrier = property.needsBarrier(constraints());
return storeSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
value, needsBarrier, slotType);
}
bool
@ -6748,7 +6700,7 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index)
// Emit GetElementCache.
types::TemporaryTypeSet *types = bytecodeTypes(pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, types);
bool barrier = PropertyReadNeedsTypeBarrier(context(), constraints(), obj, nullptr, types);
// Always add a barrier if the index might be a string, so that the cache
// can attach stubs for particular properties.
@ -6796,7 +6748,7 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
return false;
}
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, types);
bool barrier = PropertyReadNeedsTypeBarrier(context(), constraints(), obj, nullptr, types);
bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
// Reads which are on holes in the object do not have to bail out if
@ -7591,7 +7543,7 @@ IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name,
return false;
types::TypeObjectKey *type = types->getObject(0);
if (type->unknownProperties())
if (type->unknownProperties() || type->singleton())
return false;
jsid id = NameToId(name);
@ -7639,6 +7591,12 @@ TestTypeHasOwnProperty(types::TypeObjectKey *typeObj, PropertyName *name, bool &
types::HeapTypeSetKey propSet = typeObj->property(NameToId(name));
if (propSet.maybeTypes() && !propSet.maybeTypes()->empty())
cont = false;
if (JSObject *obj = typeObj->singleton()) {
// Global objects may have undefined singleton properties without associated
// type information.
if (types::CanHaveEmptyPropertyTypesForOwnProperty(obj))
cont = false;
}
// Note: Callers must explicitly freeze the property type set later on if optimizing.
return true;
}
@ -7802,7 +7760,7 @@ IonBuilder::freezePropTypeSets(types::TemporaryTypeSet *types,
// just checked, so propSet cannot be nullptr.
while (true) {
types::HeapTypeSetKey property = type->property(NameToId(name));
JS_ALWAYS_TRUE(!property.notEmpty(constraints()));
JS_ALWAYS_TRUE(!property.isOwnProperty(constraints()));
// Don't mark the proto. It will be held down by the shape
// guard. This allows us to use properties found on prototypes
@ -7907,7 +7865,7 @@ IonBuilder::annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetProper
continue;
types::HeapTypeSetKey ownTypes = typeObj->property(NameToId(name));
if (ownTypes.notEmpty(constraints()))
if (ownTypes.isOwnProperty(constraints()))
continue;
JSObject *singleton = nullptr;
@ -7932,10 +7890,6 @@ IonBuilder::annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetProper
if (!singleton)
continue;
bool knownConstant = false;
if (!testSingletonProperty(proto, singleton, name, &knownConstant))
return false;
// Don't add cases corresponding to non-observed pushes
if (!pushedTypes->hasType(types::Type::ObjectType(singleton)))
continue;
@ -7988,14 +7942,11 @@ IonBuilder::invalidatedIdempotentCache()
}
bool
IonBuilder::loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
IonBuilder::loadSlot(MDefinition *obj, size_t slot, size_t nfixed, MIRType rvalType,
bool barrier, types::TemporaryTypeSet *types)
{
JS_ASSERT(shape->hasDefaultGetter());
JS_ASSERT(shape->hasSlot());
if (shape->slot() < shape->numFixedSlots()) {
MLoadFixedSlot *load = MLoadFixedSlot::New(obj, shape->slot());
if (slot < nfixed) {
MLoadFixedSlot *load = MLoadFixedSlot::New(obj, slot);
current->add(load);
current->push(load);
@ -8006,7 +7957,7 @@ IonBuilder::loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
MSlots *slots = MSlots::New(obj);
current->add(slots);
MLoadSlot *load = MLoadSlot::New(slots, shape->slot() - shape->numFixedSlots());
MLoadSlot *load = MLoadSlot::New(slots, slot - nfixed);
current->add(load);
current->push(load);
@ -8015,15 +7966,22 @@ IonBuilder::loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
}
bool
IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier,
MIRType slotType /* = MIRType_None */)
IonBuilder::loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
bool barrier, types::TemporaryTypeSet *types)
{
JS_ASSERT(shape->hasDefaultSetter());
JS_ASSERT(shape->writable());
JS_ASSERT(shape->hasDefaultGetter());
JS_ASSERT(shape->hasSlot());
if (shape->slot() < shape->numFixedSlots()) {
MStoreFixedSlot *store = MStoreFixedSlot::New(obj, shape->slot(), value);
return loadSlot(obj, shape->slot(), shape->numFixedSlots(), rvalType, barrier, types);
}
bool
IonBuilder::storeSlot(MDefinition *obj, size_t slot, size_t nfixed,
MDefinition *value, bool needsBarrier,
MIRType slotType /* = MIRType_None */)
{
if (slot < nfixed) {
MStoreFixedSlot *store = MStoreFixedSlot::New(obj, slot, value);
current->add(store);
current->push(value);
if (needsBarrier)
@ -8034,7 +7992,7 @@ IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool n
MSlots *slots = MSlots::New(obj);
current->add(slots);
MStoreSlot *store = MStoreSlot::New(slots, shape->slot() - shape->numFixedSlots(), value);
MStoreSlot *store = MStoreSlot::New(slots, slot - nfixed, value);
current->add(store);
current->push(value);
if (needsBarrier)
@ -8044,6 +8002,17 @@ IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool n
return resumeAfter(store);
}
bool
IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier,
MIRType slotType /* = MIRType_None */)
{
JS_ASSERT(shape->hasDefaultSetter());
JS_ASSERT(shape->writable());
JS_ASSERT(shape->hasSlot());
return storeSlot(obj, shape->slot(), shape->numFixedSlots(), value, needsBarrier, slotType);
}
bool
IonBuilder::jsop_getprop(PropertyName *name)
{
@ -8054,7 +8023,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
return emitted;
types::TemporaryTypeSet *types = bytecodeTypes(pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(),
bool barrier = PropertyReadNeedsTypeBarrier(context(), constraints(),
current->peek(-1), name, types);
// Always use a call if we are doing the definite properties analysis and
@ -8140,14 +8109,8 @@ IonBuilder::getPropTryConstant(bool *emitted, PropertyName *name,
if (!singleton)
return true;
JSObject *global = &script()->global();
bool isConstant, testObject, testString;
if (!testSingletonPropertyTypes(current->peek(-1), singleton, global, name,
&isConstant, &testObject, &testString))
return false;
if (!isConstant)
bool testObject, testString;
if (!testSingletonPropertyTypes(current->peek(-1), singleton, name, &testObject, &testString))
return true;
MDefinition *obj = current->pop();
@ -8338,7 +8301,7 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, PropertyName *name,
MDefinition *obj = current->pop();
if (isDOM && TestShouldDOMCall(cx, objTypes, commonGetter, JSJitInfo::Getter)) {
if (isDOM && testShouldDOMCall(objTypes, commonGetter, JSJitInfo::Getter)) {
const JSJitInfo *jitinfo = commonGetter->jitInfo();
MGetDOMProperty *get = MGetDOMProperty::New(jitinfo, obj, guard);
current->add(get);
@ -8518,7 +8481,7 @@ IonBuilder::getPropTryCache(bool *emitted, PropertyName *name,
// Caches can read values from prototypes, so update the barrier to
// reflect such possible values.
if (!barrier)
barrier = PropertyReadOnPrototypeNeedsTypeBarrier(cx, constraints(), obj, name, types);
barrier = PropertyReadOnPrototypeNeedsTypeBarrier(constraints(), obj, name, types);
MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
if (barrier || IsNullOrUndefined(rvalType))
@ -8690,7 +8653,7 @@ IonBuilder::setPropTryCommonDOMSetter(bool *emitted, MDefinition *obj,
return true;
types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
if (!TestShouldDOMCall(cx, objTypes, setter, JSJitInfo::Setter))
if (!testShouldDOMCall(objTypes, setter, JSJitInfo::Setter))
return true;
// Emit SetDOMProperty.

View File

@ -351,8 +351,13 @@ class IonBuilder : public MIRGenerator
bool invalidatedIdempotentCache();
bool hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall);
bool loadSlot(MDefinition *obj, size_t slot, size_t nfixed, MIRType rvalType,
bool barrier, types::TemporaryTypeSet *types);
bool loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
bool barrier, types::TemporaryTypeSet *types);
bool storeSlot(MDefinition *obj, size_t slot, size_t nfixed,
MDefinition *value, bool needsBarrier,
MIRType slotType = MIRType_None);
bool storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier,
MIRType slotType = MIRType_None);
@ -612,7 +617,7 @@ class IonBuilder : public MIRGenerator
MTypeObjectDispatch *dispatch, MGetPropertyCache *cache,
MBasicBlock **fallbackTarget);
bool testNeedsArgumentCheck(JSContext *cx, JSFunction *target, CallInfo &callInfo);
bool testNeedsArgumentCheck(JSFunction *target, CallInfo &callInfo);
MDefinition *makeCallsiteClone(JSFunction *target, MDefinition *fun);
MCall *makeCallHelper(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite);
@ -625,18 +630,17 @@ class IonBuilder : public MIRGenerator
PropertyName *name, JSFunction **funcp,
bool isGetter, bool *isDOM,
MDefinition **guardOut);
bool testShouldDOMCall(types::TypeSet *inTypes,
JSFunction *func, JSJitInfo::OpType opType);
bool annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache,
types::TemporaryTypeSet *objTypes, types::TemporaryTypeSet *pushedTypes);
MGetPropertyCache *getInlineableGetPropertyCache(CallInfo &callInfo);
bool testSingletonProperty(JSObject *obj, JSObject *singleton, PropertyName *name,
bool *isKnownConstant);
bool testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton,
JSObject *globalObj, PropertyName *name,
bool *isKnownConstant, bool *testObject,
bool *testString);
bool testSingletonProperty(JSObject *obj, JSObject *singleton, PropertyName *name);
bool testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton, PropertyName *name,
bool *testObject, bool *testString);
bool getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name,
types::HeapTypeSetKey *property);
bool freezePropTypeSets(types::TemporaryTypeSet *types,

View File

@ -1147,11 +1147,6 @@ CanAttachNativeGetProp(typename GetPropCache::Context cx, const GetPropCache &ca
if (IsCacheableGetPropReadSlot(obj, holder, shape) ||
IsCacheableNoProperty(obj, holder, shape, pc, cache.output()))
{
// TI infers the possible types of native object properties. There's one
// edge case though: for singleton objects it does not add the initial
// "undefined" type, see the propertySet comment in jsinfer.h.
if (!cache.canMonitorSingletonUndefinedSlot(holder, shape))
return GetPropertyIC::CanAttachNone;
return GetPropertyIC::CanAttachReadSlot;
}
@ -1206,17 +1201,6 @@ GetPropertyIC::allowArrayLength(Context cx, HandleObject obj) const
return true;
}
bool
GetPropertyIC::canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const
{
// We can't monitor the return type inside an idempotent cache,
// so we don't handle this case.
return !(idempotent() &&
holder &&
holder->hasSingletonType() &&
holder->getSlot(shape->slot()).isUndefined());
}
bool
GetPropertyIC::tryAttachNative(JSContext *cx, IonScript *ion, HandleObject obj,
HandlePropertyName name, void *returnAddr, bool *emitted)

View File

@ -603,7 +603,6 @@ class GetPropertyIC : public RepatchIonCache
// Helpers for CanAttachNativeGetProp
typedef JSContext * Context;
bool canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const;
bool allowArrayLength(Context cx, HandleObject obj) const;
// Attach the proper stub, if possible

View File

@ -325,7 +325,7 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(),
bool barrier = PropertyReadNeedsTypeBarrier(context(), constraints(),
callInfo.thisArg(), nullptr, returnTypes);
if (barrier)
returnType = MIRType_Value;

View File

@ -2878,7 +2878,7 @@ jit::DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinit
}
static bool
PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints,
types::TypeObjectKey *object, PropertyName *name,
types::TypeSet *observed)
{
@ -2896,16 +2896,13 @@ PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *const
if (property.maybeTypes() && !TypeSetIncludes(observed, MIRType_Value, property.maybeTypes()))
return true;
// Type information for singleton objects is not required to reflect the
// initial 'undefined' value for native properties, in particular global
// Type information for global objects is not required to reflect the
// initial 'undefined' value for properties, in particular global
// variables declared with 'var'. Until the property is assigned a value
// other than undefined, a barrier is required.
if (name && object->singleton() && object->singleton()->isNative()) {
Shape *shape = object->singleton()->nativeLookup(cx, name);
if (shape &&
shape->hasSlot() &&
shape->hasDefaultGetter() &&
object->singleton()->nativeGetSlot(shape->slot()).isUndefined())
if (JSObject *obj = object->singleton()) {
if (name && types::CanHaveEmptyPropertyTypesForOwnProperty(obj) &&
(!property.maybeTypes() || property.maybeTypes()->empty()))
{
return true;
}
@ -2916,7 +2913,7 @@ PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *const
}
bool
jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
types::CompilerConstraintList *constraints,
types::TypeObjectKey *object, PropertyName *name,
types::TemporaryTypeSet *observed, bool updateObserved)
@ -2949,11 +2946,11 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
}
}
return PropertyReadNeedsTypeBarrier(cx, constraints, object, name, observed);
return PropertyReadNeedsTypeBarrier(constraints, object, name, observed);
}
bool
jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed)
@ -2969,7 +2966,7 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
for (size_t i = 0; i < types->getObjectCount(); i++) {
types::TypeObjectKey *object = types->getObject(i);
if (object) {
if (PropertyReadNeedsTypeBarrier(cx, propertycx, constraints, object, name,
if (PropertyReadNeedsTypeBarrier(propertycx, constraints, object, name,
observed, updateObserved))
{
return true;
@ -2981,7 +2978,7 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
}
bool
jit::PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed)
{
@ -2998,7 +2995,7 @@ jit::PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, types::CompilerConst
continue;
while (object->proto().isObject()) {
object = types::TypeObjectKey::get(object->proto().toObject());
if (PropertyReadNeedsTypeBarrier(cx, constraints, object, name, observed))
if (PropertyReadNeedsTypeBarrier(constraints, object, name, observed))
return true;
}
}

View File

@ -9135,15 +9135,15 @@ bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefiniti
bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints,
MDefinition *obj);
MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj);
bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
bool PropertyReadNeedsTypeBarrier(JSContext *propertycx,
types::CompilerConstraintList *constraints,
types::TypeObjectKey *object, PropertyName *name,
types::TemporaryTypeSet *observed, bool updateObserved);
bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
bool PropertyReadNeedsTypeBarrier(JSContext *propertycx,
types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed);
bool PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
bool PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed);
bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,

View File

@ -718,8 +718,7 @@ extern JS_FRIEND_API(bool)
IsContextRunningJS(JSContext *cx);
typedef bool
(* DOMInstanceClassMatchesProto)(JS::HandleObject protoObject, uint32_t protoID,
uint32_t depth);
(* DOMInstanceClassMatchesProto)(JSObject *protoObject, uint32_t protoID, uint32_t depth);
struct JSDOMCallbacks {
DOMInstanceClassMatchesProto instanceClassMatchesProto;
};

View File

@ -1105,10 +1105,14 @@ HeapTypeSetKey::knownTypeTag(CompilerConstraintList *constraints)
}
bool
HeapTypeSetKey::notEmpty(CompilerConstraintList *constraints)
HeapTypeSetKey::isOwnProperty(CompilerConstraintList *constraints)
{
if (maybeTypes() && !maybeTypes()->empty())
return true;
if (JSObject *obj = object()->singleton()) {
if (CanHaveEmptyPropertyTypesForOwnProperty(obj))
return true;
}
freeze(constraints);
return false;
}
@ -1962,7 +1966,7 @@ PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj)
if (type->unknownProperties())
return true;
HeapTypeSetKey index = type->property(JSID_VOID);
if (index.configured(constraints, type) || index.notEmpty(constraints))
if (index.configured(constraints, type) || index.isOwnProperty(constraints))
return true;
obj = obj->getProto();
} while (obj);
@ -2610,8 +2614,8 @@ TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t n
/////////////////////////////////////////////////////////////////////
static inline void
UpdatePropertyType(ExclusiveContext *cx, TypeSet *types, JSObject *obj, Shape *shape,
bool force)
UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shape *shape,
bool indexed)
{
if (!shape->writable())
types->setConfiguredProperty(cx);
@ -2620,13 +2624,17 @@ UpdatePropertyType(ExclusiveContext *cx, TypeSet *types, JSObject *obj, Shape *s
types->setConfiguredProperty(cx);
types->addType(cx, Type::UnknownType());
} else if (shape->hasDefaultGetter() && shape->hasSlot()) {
if (!indexed && types->canSetDefinite(shape->slot()))
types->setDefinite(shape->slot());
const Value &value = obj->nativeGetSlot(shape->slot());
/*
* Don't add initial undefined types for singleton properties that are
* not collated into the JSID_VOID property (see propertySet comment).
* Don't add initial undefined types for properties of global objects
* that are not collated into the JSID_VOID property (see propertySet
* comment).
*/
if (force || !value.isUndefined()) {
if (indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) {
Type type = GetValueType(value);
types->addType(cx, type);
}
@ -2706,9 +2714,7 @@ TypeObject::addDefiniteProperties(ExclusiveContext *cx, JSObject *obj)
RootedShape shape(cx, obj->lastProperty());
while (!shape->isEmptyShape()) {
jsid id = IdToTypeId(shape->propid());
if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot()) &&
shape->slot() <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT))
{
if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot())) {
TypeSet *types = getProperty(cx, id);
if (!types)
return false;

View File

@ -382,18 +382,19 @@ enum {
* differently from a normal native property (e.g. made non-writable or
* given a scripted getter or setter).
*/
TYPE_FLAG_CONFIGURED_PROPERTY = 0x00200000,
TYPE_FLAG_CONFIGURED_PROPERTY = 0x00080000,
/*
* Whether the property is definitely in a particular inline slot on all
* objects from which it has not been deleted or reconfigured. Implies
* OWN_PROPERTY and unlike OWN/CONFIGURED property, this cannot change.
* Whether the property is definitely in a particular slot on all objects
* from which it has not been deleted or reconfigured. For singletons
* this may be a fixed or dynamic slot, and for other objects this will be
* a fixed slot.
*/
TYPE_FLAG_DEFINITE_PROPERTY = 0x00400000,
TYPE_FLAG_DEFINITE_PROPERTY = 0x00100000,
/* If the property is definite, mask and shift storing the slot. */
TYPE_FLAG_DEFINITE_MASK = 0x0f000000,
TYPE_FLAG_DEFINITE_SHIFT = 24
TYPE_FLAG_DEFINITE_MASK = 0xffe00000,
TYPE_FLAG_DEFINITE_SHIFT = 21
};
typedef uint32_t TypeFlags;
@ -565,8 +566,11 @@ class TypeSet
void setConfiguredProperty() {
flags |= TYPE_FLAG_CONFIGURED_PROPERTY;
}
bool canSetDefinite(unsigned slot) {
return slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT);
}
void setDefinite(unsigned slot) {
JS_ASSERT(slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT));
JS_ASSERT(canSetDefinite(slot));
flags |= TYPE_FLAG_DEFINITE_PROPERTY | (slot << TYPE_FLAG_DEFINITE_SHIFT);
}
@ -968,7 +972,7 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
* 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 singleton JS objects which
* 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
@ -977,8 +981,8 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
* or deletion.
*
* We establish these by using write barriers on calls to setProperty and
* defineProperty which are on native properties, and by using the inference
* analysis to determine the side effects of code which is JIT-compiled.
* defineProperty which are on native properties, and on any jitcode which
* might update the property with a new type.
*/
Property **propertySet;
@ -1315,7 +1319,7 @@ class HeapTypeSetKey
void freeze(CompilerConstraintList *constraints);
JSValueType knownTypeTag(CompilerConstraintList *constraints);
bool configured(CompilerConstraintList *constraints, TypeObjectKey *type);
bool notEmpty(CompilerConstraintList *constraints);
bool isOwnProperty(CompilerConstraintList *constraints);
bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other);
JSObject *singleton(CompilerConstraintList *constraints);
bool needsBarrier(CompilerConstraintList *constraints);

View File

@ -440,6 +440,15 @@ EnsureTrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
JS_ASSERT(obj->type()->unknownProperties() || TrackPropertyTypes(cx, obj, id));
}
inline bool
CanHaveEmptyPropertyTypesForOwnProperty(JSObject *obj)
{
// Per the comment on TypeSet::propertySet, property type sets for global
// objects may be empty for 'own' properties if the global property still
// has its initial undefined value.
return obj->is<GlobalObject>();
}
inline bool
HasTypePropertyId(JSObject *obj, jsid id, Type type)
{

View File

@ -5021,7 +5021,7 @@ dom_constructor(JSContext* cx, unsigned argc, JS::Value *vp)
}
static bool
InstanceClassHasProtoAtDepth(HandleObject protoObject, uint32_t protoID, uint32_t depth)
InstanceClassHasProtoAtDepth(JSObject *protoObject, uint32_t protoID, uint32_t depth)
{
/* There's only a single (fake) DOM object in the shell, so just return true. */
return true;