Bug 1073766 - Guard on the global's shape, when optimizing on its proto-chain. r=bhackett

This commit is contained in:
Johannes Schulte 2014-10-24 12:36:01 +02:00
parent 1a481b0b30
commit 3d3db0eaa4
6 changed files with 70 additions and 24 deletions

View File

@ -479,8 +479,20 @@ BaselineInspector::templateCallObject()
return &res->as<CallObject>(); return &res->as<CallObject>();
} }
static Shape *GlobalShapeForGetPropFunction(ICStub *stub)
{
if (stub->isGetProp_CallNativePrototype()) {
ICGetProp_CallNativePrototype *nstub =
stub->toGetProp_CallNativePrototype();
if (nstub->receiverShape()->getObjectClass()->flags & JSCLASS_IS_GLOBAL)
return nstub->receiverShape();
}
return nullptr;
}
JSObject * JSObject *
BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter) BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter,
Shape **globalShape)
{ {
if (!hasBaselineScript()) if (!hasBaselineScript())
return nullptr; return nullptr;
@ -489,6 +501,7 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J
JSObject* holder = nullptr; JSObject* holder = nullptr;
Shape *holderShape = nullptr; Shape *holderShape = nullptr;
JSFunction *getter = nullptr; JSFunction *getter = nullptr;
Shape *global = nullptr;
for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
if (stub->isGetProp_CallScripted() || if (stub->isGetProp_CallScripted() ||
stub->isGetProp_CallNative() || stub->isGetProp_CallNative() ||
@ -499,7 +512,10 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J
holder = nstub->holder(); holder = nstub->holder();
holderShape = nstub->holderShape(); holderShape = nstub->holderShape();
getter = nstub->getter(); getter = nstub->getter();
} else if (nstub->holderShape() != holderShape) { global = GlobalShapeForGetPropFunction(nstub);
} else if (nstub->holderShape() != holderShape ||
GlobalShapeForGetPropFunction(nstub) != global)
{
return nullptr; return nullptr;
} else { } else {
MOZ_ASSERT(getter == nstub->getter()); MOZ_ASSERT(getter == nstub->getter());
@ -513,6 +529,7 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J
} }
*lastProperty = holderShape; *lastProperty = holderShape;
*commonGetter = getter; *commonGetter = getter;
*globalShape = global;
return holder; return holder;
} }

View File

@ -115,7 +115,8 @@ class BaselineInspector
DeclEnvObject *templateDeclEnvObject(); DeclEnvObject *templateDeclEnvObject();
CallObject *templateCallObject(); CallObject *templateCallObject();
JSObject *commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter); JSObject *commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter,
Shape **globalShape);
JSObject *commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter); JSObject *commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter);
}; };

View File

@ -8770,7 +8770,7 @@ IonBuilder::jsop_not()
bool bool
IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name, IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
bool isGetter, JSObject *foundProto) bool isGetter, JSObject *foundProto, bool *guardGlobal)
{ {
// With foundProto a prototype with a getter or setter for name, return // With foundProto a prototype with a getter or setter for name, return
// whether looking up name on any object in |types| will go through // whether looking up name on any object in |types| will go through
@ -8780,6 +8780,7 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN
// No sense looking if we don't know what's going on. // No sense looking if we don't know what's going on.
if (!types || types->unknownObject()) if (!types || types->unknownObject())
return false; return false;
*guardGlobal = false;
for (unsigned i = 0; i < types->getObjectCount(); i++) { for (unsigned i = 0; i < types->getObjectCount(); i++) {
if (types->getSingleObject(i) == foundProto) if (types->getSingleObject(i) == foundProto)
@ -8794,8 +8795,14 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN
return false; return false;
const Class *clasp = type->clasp(); const Class *clasp = type->clasp();
if (!ClassHasEffectlessLookup(clasp, name) || ClassHasResolveHook(compartment, clasp, name)) if (!ClassHasEffectlessLookup(clasp, name))
return false; return false;
JSObject *singleton = type->singleton();
if (ClassHasResolveHook(compartment, clasp, name)) {
if (!singleton || !singleton->is<GlobalObject>())
return false;
*guardGlobal = true;
}
// Look for a getter/setter on the class itself which may need // Look for a getter/setter on the class itself which may need
// to be called. Ignore the getGeneric hook for typed arrays, it // to be called. Ignore the getGeneric hook for typed arrays, it
@ -8813,9 +8820,11 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN
if (!types->empty() || types->nonDataProperty()) if (!types->empty() || types->nonDataProperty())
return false; return false;
} }
if (JSObject *obj = type->singleton()) { if (singleton) {
if (types::CanHaveEmptyPropertyTypesForOwnProperty(obj)) if (types::CanHaveEmptyPropertyTypesForOwnProperty(singleton)) {
return false; MOZ_ASSERT(singleton->is<GlobalObject>());
*guardGlobal = true;
}
} }
if (!type->hasTenuredProto()) if (!type->hasTenuredProto())
@ -8837,7 +8846,8 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN
void void
IonBuilder::freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name, IonBuilder::freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
JSObject *foundProto) JSObject *foundProto,
bool allowEmptyTypesforGlobal/* = false*/)
{ {
for (unsigned i = 0; i < types->getObjectCount(); i++) { for (unsigned i = 0; i < types->getObjectCount(); i++) {
// If we found a Singleton object's own-property, there's nothing to // If we found a Singleton object's own-property, there's nothing to
@ -8851,7 +8861,7 @@ IonBuilder::freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, P
while (true) { while (true) {
types::HeapTypeSetKey property = type->property(NameToId(name)); types::HeapTypeSetKey property = type->property(NameToId(name));
JS_ALWAYS_TRUE(!property.isOwnProperty(constraints())); JS_ALWAYS_TRUE(!property.isOwnProperty(constraints(), allowEmptyTypesforGlobal));
// Don't mark the proto. It will be held down by the shape // Don't mark the proto. It will be held down by the shape
// guard. This allows us to use properties found on prototypes // guard. This allows us to use properties found on prototypes
@ -8865,21 +8875,34 @@ IonBuilder::freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, P
inline MDefinition * inline MDefinition *
IonBuilder::testCommonGetterSetter(types::TemporaryTypeSet *types, PropertyName *name, IonBuilder::testCommonGetterSetter(types::TemporaryTypeSet *types, PropertyName *name,
bool isGetter, JSObject *foundProto, Shape *lastProperty) bool isGetter, JSObject *foundProto, Shape *lastProperty,
Shape *globalShape/* = nullptr*/)
{ {
bool guardGlobal;
// Check if all objects being accessed will lookup the name through foundProto. // Check if all objects being accessed will lookup the name through foundProto.
if (!objectsHaveCommonPrototype(types, name, isGetter, foundProto)) if (!objectsHaveCommonPrototype(types, name, isGetter, foundProto, &guardGlobal) ||
(guardGlobal && !globalShape))
{
return nullptr; return nullptr;
}
// We can optimize the getter/setter, so freeze all involved properties to // We can optimize the getter/setter, so freeze all involved properties to
// ensure there isn't a lower shadowing getter or setter installed in the // ensure there isn't a lower shadowing getter or setter installed in the
// future. // future.
freezePropertiesForCommonPrototype(types, name, foundProto); freezePropertiesForCommonPrototype(types, name, foundProto, guardGlobal);
// Add a shape guard on the prototype we found the property on. The rest of // Add a shape guard on the prototype we found the property on. The rest of
// the prototype chain is guarded by TI freezes. Note that a shape guard is // the prototype chain is guarded by TI freezes, except when name is a global
// good enough here, even in the proxy case, because we have ensured there // name. In this case, we also have to guard on the globals shape to be able
// are no lookup hooks for this property. // to optimize. Note that a shape guard is good enough here, even in the proxy
// case, because we have ensured there are no lookup hooks for this property.
if (guardGlobal) {
JSObject *obj = &script()->global();
MDefinition *globalObj = constant(ObjectValue(*obj));
addShapeGuard(globalObj, globalShape, Bailout_ShapeGuard);
}
MInstruction *wrapper = constant(ObjectValue(*foundProto)); MInstruction *wrapper = constant(ObjectValue(*foundProto));
return addShapeGuard(wrapper, lastProperty, Bailout_ShapeGuard); return addShapeGuard(wrapper, lastProperty, Bailout_ShapeGuard);
} }
@ -9407,13 +9430,14 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName
Shape *lastProperty = nullptr; Shape *lastProperty = nullptr;
JSFunction *commonGetter = nullptr; JSFunction *commonGetter = nullptr;
JSObject *foundProto = inspector->commonGetPropFunction(pc, &lastProperty, &commonGetter); Shape *globalShape = nullptr;
JSObject *foundProto = inspector->commonGetPropFunction(pc, &lastProperty, &commonGetter, &globalShape);
if (!foundProto) if (!foundProto)
return true; return true;
types::TemporaryTypeSet *objTypes = obj->resultTypeSet(); types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
MDefinition *guard = testCommonGetterSetter(objTypes, name, /* isGetter = */ true, MDefinition *guard = testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
foundProto, lastProperty); foundProto, lastProperty, globalShape);
if (!guard) if (!guard)
return true; return true;

View File

@ -807,11 +807,12 @@ class IonBuilder
MBasicBlock *bottom); MBasicBlock *bottom);
bool objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name, bool objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
bool isGetter, JSObject *foundProto); bool isGetter, JSObject *foundProto, bool *guardGlobal);
void freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name, void freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
JSObject *foundProto); JSObject *foundProto, bool allowEmptyTypesForGlobal = false);
MDefinition *testCommonGetterSetter(types::TemporaryTypeSet *types, PropertyName *name, MDefinition *testCommonGetterSetter(types::TemporaryTypeSet *types, PropertyName *name,
bool isGetter, JSObject *foundProto, Shape *lastProperty); bool isGetter, JSObject *foundProto, Shape *lastProperty,
Shape *globalShape = nullptr);
bool testShouldDOMCall(types::TypeSet *inTypes, bool testShouldDOMCall(types::TypeSet *inTypes,
JSFunction *func, JSJitInfo::OpType opType); JSFunction *func, JSJitInfo::OpType opType);

View File

@ -1446,11 +1446,14 @@ HeapTypeSetKey::knownMIRType(CompilerConstraintList *constraints)
} }
bool bool
HeapTypeSetKey::isOwnProperty(CompilerConstraintList *constraints) HeapTypeSetKey::isOwnProperty(CompilerConstraintList *constraints,
bool allowEmptyTypesForGlobal/* = false*/)
{ {
if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty())) if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty()))
return true; return true;
if (JSObject *obj = object()->singleton()) { JSObject *obj = object()->singleton();
MOZ_ASSERT_IF(obj, CanHaveEmptyPropertyTypesForOwnProperty(obj) == obj->is<GlobalObject>());
if (obj && !allowEmptyTypesForGlobal) {
if (CanHaveEmptyPropertyTypesForOwnProperty(obj)) if (CanHaveEmptyPropertyTypesForOwnProperty(obj))
return true; return true;
} }

View File

@ -1566,7 +1566,7 @@ class HeapTypeSetKey
jit::MIRType knownMIRType(CompilerConstraintList *constraints); jit::MIRType knownMIRType(CompilerConstraintList *constraints);
bool nonData(CompilerConstraintList *constraints); bool nonData(CompilerConstraintList *constraints);
bool nonWritable(CompilerConstraintList *constraints); bool nonWritable(CompilerConstraintList *constraints);
bool isOwnProperty(CompilerConstraintList *constraints); bool isOwnProperty(CompilerConstraintList *constraints, bool allowEmptyTypesForGlobal = false);
bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other); bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other);
JSObject *singleton(CompilerConstraintList *constraints); JSObject *singleton(CompilerConstraintList *constraints);
bool needsBarrier(CompilerConstraintList *constraints); bool needsBarrier(CompilerConstraintList *constraints);