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>();
}
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 *
BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter)
BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter,
Shape **globalShape)
{
if (!hasBaselineScript())
return nullptr;
@ -489,6 +501,7 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J
JSObject* holder = nullptr;
Shape *holderShape = nullptr;
JSFunction *getter = nullptr;
Shape *global = nullptr;
for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
if (stub->isGetProp_CallScripted() ||
stub->isGetProp_CallNative() ||
@ -499,7 +512,10 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J
holder = nstub->holder();
holderShape = nstub->holderShape();
getter = nstub->getter();
} else if (nstub->holderShape() != holderShape) {
global = GlobalShapeForGetPropFunction(nstub);
} else if (nstub->holderShape() != holderShape ||
GlobalShapeForGetPropFunction(nstub) != global)
{
return nullptr;
} else {
MOZ_ASSERT(getter == nstub->getter());
@ -513,6 +529,7 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J
}
*lastProperty = holderShape;
*commonGetter = getter;
*globalShape = global;
return holder;
}

View File

@ -115,7 +115,8 @@ class BaselineInspector
DeclEnvObject *templateDeclEnvObject();
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);
};

View File

@ -8770,7 +8770,7 @@ IonBuilder::jsop_not()
bool
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
// 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.
if (!types || types->unknownObject())
return false;
*guardGlobal = false;
for (unsigned i = 0; i < types->getObjectCount(); i++) {
if (types->getSingleObject(i) == foundProto)
@ -8794,8 +8795,14 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN
return false;
const Class *clasp = type->clasp();
if (!ClassHasEffectlessLookup(clasp, name) || ClassHasResolveHook(compartment, clasp, name))
if (!ClassHasEffectlessLookup(clasp, name))
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
// 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())
return false;
}
if (JSObject *obj = type->singleton()) {
if (types::CanHaveEmptyPropertyTypesForOwnProperty(obj))
return false;
if (singleton) {
if (types::CanHaveEmptyPropertyTypesForOwnProperty(singleton)) {
MOZ_ASSERT(singleton->is<GlobalObject>());
*guardGlobal = true;
}
}
if (!type->hasTenuredProto())
@ -8837,7 +8846,8 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN
void
IonBuilder::freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
JSObject *foundProto)
JSObject *foundProto,
bool allowEmptyTypesforGlobal/* = false*/)
{
for (unsigned i = 0; i < types->getObjectCount(); i++) {
// 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) {
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
// guard. This allows us to use properties found on prototypes
@ -8865,21 +8875,34 @@ IonBuilder::freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, P
inline MDefinition *
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.
if (!objectsHaveCommonPrototype(types, name, isGetter, foundProto))
if (!objectsHaveCommonPrototype(types, name, isGetter, foundProto, &guardGlobal) ||
(guardGlobal && !globalShape))
{
return nullptr;
}
// 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
// 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
// the prototype chain is guarded by TI freezes. 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.
// the prototype chain is guarded by TI freezes, except when name is a global
// name. In this case, we also have to guard on the globals shape to be able
// 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));
return addShapeGuard(wrapper, lastProperty, Bailout_ShapeGuard);
}
@ -9407,13 +9430,14 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName
Shape *lastProperty = nullptr;
JSFunction *commonGetter = nullptr;
JSObject *foundProto = inspector->commonGetPropFunction(pc, &lastProperty, &commonGetter);
Shape *globalShape = nullptr;
JSObject *foundProto = inspector->commonGetPropFunction(pc, &lastProperty, &commonGetter, &globalShape);
if (!foundProto)
return true;
types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
MDefinition *guard = testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
foundProto, lastProperty);
foundProto, lastProperty, globalShape);
if (!guard)
return true;

View File

@ -807,11 +807,12 @@ class IonBuilder
MBasicBlock *bottom);
bool objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
bool isGetter, JSObject *foundProto);
bool isGetter, JSObject *foundProto, bool *guardGlobal);
void freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
JSObject *foundProto);
JSObject *foundProto, bool allowEmptyTypesForGlobal = false);
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,
JSFunction *func, JSJitInfo::OpType opType);

View File

@ -1446,11 +1446,14 @@ HeapTypeSetKey::knownMIRType(CompilerConstraintList *constraints)
}
bool
HeapTypeSetKey::isOwnProperty(CompilerConstraintList *constraints)
HeapTypeSetKey::isOwnProperty(CompilerConstraintList *constraints,
bool allowEmptyTypesForGlobal/* = false*/)
{
if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty()))
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))
return true;
}

View File

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