Bug 923693 - Distinguish different kinds of object state changes in type information, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 10 Oct 2013 07:45:20 -0600
changeset 150363 f868d4f8f27e489485aee5063c7e165328883c86
parent 150362 266e7e5e3a2b484ae6de3d1b73fa2d0f3935f210
child 150364 44eee084be1d44dc09992cc8d224437f4b8e7c9c
push id25437
push userkwierso@gmail.com
push dateFri, 11 Oct 2013 02:00:22 +0000
treeherdermozilla-central@672cd63528d3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs923693
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 923693 - Distinguish different kinds of object state changes in type information, r=jandem.
js/src/jit/IonBuilder.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -3955,17 +3955,17 @@ IonBuilder::makeInliningDecision(JSFunct
             IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee is not hot.",
                     targetScript->filename(), targetScript->lineno);
             return false;
         }
     }
 
     // TI calls ObjectStateChange to trigger invalidation of the caller.
     types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
-    targetType->watchStateChange(constraints());
+    targetType->watchStateChangeForInlinedCall(constraints());
 
     return true;
 }
 
 uint32_t
 IonBuilder::selectInliningTargets(ObjectVector &targets, CallInfo &callInfo, BoolVector &choiceSet)
 {
     uint32_t totalSize = 0;
@@ -4638,17 +4638,17 @@ IonBuilder::createThisScriptedSingleton(
     RootedObject targetRoot(cx, target);
     JSObject *templateObject = CreateThisForFunctionWithProto(cx, targetRoot, proto, TenuredObject);
     if (!templateObject)
         return nullptr;
 
     // Trigger recompilation if the templateObject changes.
     types::TypeObjectKey *templateType = types::TypeObjectKey::get(templateObject);
     if (templateType->newScript())
-        templateType->watchStateChange(constraints());
+        templateType->watchStateChangeForNewScriptTemplate(constraints());
 
     MCreateThisWithTemplate *createThis = MCreateThisWithTemplate::New(templateObject);
     current->add(createThis);
 
     return createThis;
 }
 
 MDefinition *
@@ -6881,17 +6881,17 @@ IonBuilder::getTypedArrayElements(MDefin
 {
     if (obj->isConstant() && obj->toConstant()->value().isObject()) {
         TypedArrayObject *tarr = &obj->toConstant()->value().toObject().as<TypedArrayObject>();
         void *data = tarr->viewData();
 
         // The 'data' pointer can change in rare circumstances
         // (ArrayBufferObject::changeContents).
         types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr);
-        tarrType->watchStateChange(constraints());
+        tarrType->watchStateChangeForTypedArrayBuffer(constraints());
 
         obj->setFoldedUnchecked();
         return MConstantElements::New(data);
     }
     return MTypedArrayElements::New(obj);
 }
 
 MDefinition *
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -623,26 +623,32 @@ class TypeCompilerConstraint : public Ty
             cx->compartment()->types.addPendingRecompile(cx, compilation);
     }
 
     void newPropertyState(JSContext *cx, TypeSet *source) {
         if (data.invalidateOnNewPropertyState(source))
             cx->compartment()->types.addPendingRecompile(cx, compilation);
     }
 
-    void newObjectState(JSContext *cx, TypeObject *object, bool force) {
-        if (data.invalidateOnNewObjectState(object, force))
+    void newObjectState(JSContext *cx, TypeObject *object) {
+        // Note: Once the object has unknown properties, no more notifications
+        // will be sent on changes to its state, so always invalidate any
+        // associated compilations.
+        if (object->unknownProperties() || data.invalidateOnNewObjectState(object))
             cx->compartment()->types.addPendingRecompile(cx, compilation);
     }
 };
 
 template <typename T>
 bool
 CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo)
 {
+    if (property.actualObject->unknownProperties())
+        return false;
+
     if (!data.constraintHolds(cx, property, expected))
         return false;
 
     property.actualTypes->add(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
                               /* callExisting = */ false);
     return true;
 }
 
@@ -695,16 +701,17 @@ HeapTypeSetKey
 TypeObjectKey::property(jsid id)
 {
 #ifdef JS_ION
     JSContext *cx = jit::GetIonContext()->cx;
     TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
     if (!type)
         MOZ_CRASH();
     HeapTypeSetKey property;
+    property.actualObject = type;
     property.actualTypes = type->getProperty(cx, id);
     if (!property.actualTypes)
         MOZ_CRASH();
     return property;
 #else
     MOZ_CRASH();
 #endif
 }
@@ -749,17 +756,17 @@ class ConstraintDataFreeze
 {
   public:
     ConstraintDataFreeze() {}
 
     const char *kind() { return "freeze"; }
 
     bool invalidateOnNewType(Type type) { return true; }
     bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
-    bool invalidateOnNewObjectState(TypeObject *object, bool force) { return false; }
+    bool invalidateOnNewObjectState(TypeObject *object) { return false; }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return property.actualTypes->isSubset(expected);
     }
 };
 
@@ -914,43 +921,37 @@ HeapTypeSetKey::needsBarrier(CompilerCon
 }
 
 namespace {
 
 // Constraint which triggers recompilation if an object acquires particular flags.
 class ConstraintDataFreezeObjectFlags
 {
   public:
-    // Object being queried.
-    TypeObjectKey *object;
-
     // Flags we are watching for on this object.
     TypeObjectFlags flags;
 
-    ConstraintDataFreezeObjectFlags(TypeObjectKey *object, TypeObjectFlags flags)
-      : object(object), flags(flags)
-    {}
+    ConstraintDataFreezeObjectFlags(TypeObjectFlags flags)
+      : flags(flags)
+    {
+        JS_ASSERT(flags);
+    }
 
     const char *kind() { return "freezeObjectFlags"; }
 
     bool invalidateOnNewType(Type type) { return false; }
     bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
-    bool invalidateOnNewObjectState(TypeObject *object, bool force) {
-        return flags ? object->hasAnyFlags(flags) : force;
+    bool invalidateOnNewObjectState(TypeObject *object) {
+        return object->hasAnyFlags(flags);
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
-        // FIXME: There is not yet any way to test if constraints with no
-        // associated flags (i.e. those invalidated via |force|) still hold.
-        TypeObject *type = object->isSingleObject()
-                           ? object->asSingleObject()->type()
-                           : object->asTypeObject();
-        return !type->hasAnyFlags(flags);
+        return !invalidateOnNewObjectState(property.actualObject);
     }
 };
 
 } /* anonymous namespace */
 
 bool
 TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
 {
@@ -960,30 +961,23 @@ TypeObjectKey::hasFlags(CompilerConstrai
     JSContext *cx = jit::GetIonContext()->cx;
     TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
     if (!type)
         MOZ_CRASH();
     if (type->hasAnyFlags(flags))
         return true;
 
     HeapTypeSetKey objectProperty = property(JSID_EMPTY);
-    constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(this, flags)));
+    constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(flags)));
     return false;
 #else
     MOZ_CRASH();
 #endif
 }
 
-void
-TypeObjectKey::watchStateChange(CompilerConstraintList *constraints)
-{
-    HeapTypeSetKey objectProperty = property(JSID_EMPTY);
-    constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(this, 0)));
-}
-
 bool
 TemporaryTypeSet::hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
 {
     if (unknownObject())
         return true;
 
     /*
      * Treat type sets containing no objects as having all object flags,
@@ -997,34 +991,140 @@ TemporaryTypeSet::hasObjectFlags(Compile
         TypeObjectKey *object = getObject(i);
         if (object && object->hasFlags(constraints, flags))
             return true;
     }
 
     return false;
 }
 
-static inline void
-ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnknown, bool force)
+namespace {
+
+// Constraint which triggers recompilation on any type change in an inlined
+// script. The freeze constraints added to stack type sets will only directly
+// invalidate the script containing those stack type sets. To invalidate code
+// for scripts into which the base script was inlined, ObjectStateChange is used.
+class ConstraintDataFreezeObjectForInlinedCall
+{
+  public:
+    ConstraintDataFreezeObjectForInlinedCall()
+    {}
+
+    const char *kind() { return "freezeObjectForInlinedCall"; }
+
+    bool invalidateOnNewType(Type type) { return false; }
+    bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
+    bool invalidateOnNewObjectState(TypeObject *object) {
+        // We don't keep track of the exact dependencies the caller has on its
+        // inlined scripts' type sets, so always invalidate the caller.
+        return true;
+    }
+
+    bool constraintHolds(JSContext *cx,
+                         const HeapTypeSetKey &property, TemporaryTypeSet *expected)
+    {
+        return true;
+    }
+};
+
+// Constraint which triggers recompilation when the allocation kind of the
+// template object for a type's new script changes.
+class ConstraintDataFreezeObjectForNewScriptTemplate
+{
+    gc::AllocKind allocKind;
+
+  public:
+    ConstraintDataFreezeObjectForNewScriptTemplate(gc::AllocKind allocKind)
+      : allocKind(allocKind)
+    {}
+
+    const char *kind() { return "freezeObjectForNewScriptTemplate"; }
+
+    bool invalidateOnNewType(Type type) { return false; }
+    bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
+    bool invalidateOnNewObjectState(TypeObject *object) {
+        return !object->hasNewScript() || object->newScript()->allocKind != allocKind;
+    }
+
+    bool constraintHolds(JSContext *cx,
+                         const HeapTypeSetKey &property, TemporaryTypeSet *expected)
+    {
+        return !invalidateOnNewObjectState(property.actualObject);
+    }
+};
+
+// Constraint which triggers recompilation when the underlying data pointer for
+// a typed array changes.
+class ConstraintDataFreezeObjectForTypedArrayBuffer
+{
+    void *viewData;
+
+  public:
+    ConstraintDataFreezeObjectForTypedArrayBuffer(void *viewData)
+      : viewData(viewData)
+    {}
+
+    const char *kind() { return "freezeObjectForTypedArrayBuffer"; }
+
+    bool invalidateOnNewType(Type type) { return false; }
+    bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
+    bool invalidateOnNewObjectState(TypeObject *object) {
+        return object->singleton->as<TypedArrayObject>().viewData() != viewData;
+    }
+
+    bool constraintHolds(JSContext *cx,
+                         const HeapTypeSetKey &property, TemporaryTypeSet *expected)
+    {
+        return !invalidateOnNewObjectState(property.actualObject);
+    }
+};
+
+} /* anonymous namespace */
+
+void
+TypeObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList *constraints)
+{
+    HeapTypeSetKey objectProperty = property(JSID_EMPTY);
+    constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectForInlinedCall> >(objectProperty, ConstraintDataFreezeObjectForInlinedCall()));
+}
+
+void
+TypeObjectKey::watchStateChangeForNewScriptTemplate(CompilerConstraintList *constraints)
+{
+    gc::AllocKind kind = asTypeObject()->newScript()->allocKind;
+    HeapTypeSetKey objectProperty = property(JSID_EMPTY);
+    constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectForNewScriptTemplate> >(objectProperty, ConstraintDataFreezeObjectForNewScriptTemplate(kind)));
+}
+
+void
+TypeObjectKey::watchStateChangeForTypedArrayBuffer(CompilerConstraintList *constraints)
+{
+    void *viewData = asSingleObject()->as<TypedArrayObject>().viewData();
+    HeapTypeSetKey objectProperty = property(JSID_EMPTY);
+    constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectForTypedArrayBuffer> >(objectProperty, ConstraintDataFreezeObjectForTypedArrayBuffer(viewData)));
+}
+
+static void
+ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnknown)
 {
     if (object->unknownProperties())
         return;
 
     /* All constraints listening to state changes are on the empty id. */
     TypeSet *types = object->maybeGetProperty(JSID_EMPTY);
 
     /* Mark as unknown after getting the types, to avoid assertion. */
     if (markingUnknown)
         object->flags |= OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES;
 
     if (types) {
         if (JSContext *cx = cxArg->maybeJSContext()) {
             TypeConstraint *constraint = types->constraintList;
             while (constraint) {
-                constraint->newObjectState(cx, object, force);
+                constraint->newObjectState(cx, object);
                 constraint = constraint->next;
             }
         } else {
             JS_ASSERT(!types->constraintList);
         }
     }
 }
 
@@ -1043,17 +1143,17 @@ class ConstraintDataFreezeConfiguredProp
     {}
 
     const char *kind() { return "freezeConfiguredProperty"; }
 
     bool invalidateOnNewType(Type type) { return false; }
     bool invalidateOnNewPropertyState(TypeSet *property) {
         return property->configuredProperty();
     }
-    bool invalidateOnNewObjectState(TypeObject *object, bool force) { return false; }
+    bool invalidateOnNewObjectState(TypeObject *object) { return false; }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         // Everywhere compiled code depends on definite properties associated
         // with a type object's newScript, we need to make sure there are
         // constraints in place which will mark those properties as configured
         // should the definite properties be invalidated.
@@ -1323,26 +1423,16 @@ TemporaryTypeSet::propertyNeedsBarrier(C
         HeapTypeSetKey property = type->property(id);
         if (property.needsBarrier(constraints))
             return true;
     }
 
     return false;
 }
 
-/*
- * Force recompilation of any jitcode for the script, or of any other script
- * which this script was inlined into.
- */
-static inline void
-AddPendingRecompile(JSContext *cx, JSScript *script)
-{
-    cx->compartment()->types.addPendingRecompile(cx, script);
-}
-
 namespace {
 
 /*
  * As for TypeConstraintFreeze, but describes an implicit freeze constraint
  * added for stack types within a script. Applies to all compilations of the
  * script, not just a single one.
  */
 class TypeConstraintFreezeStack : public TypeConstraint
@@ -1357,17 +1447,17 @@ class TypeConstraintFreezeStack : public
     const char *kind() { return "freezeStack"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
         /*
          * Unlike TypeConstraintFreeze, triggering this constraint once does
          * not disable it on future changes to the type set.
          */
-        AddPendingRecompile(cx, script_);
+        cx->compartment()->types.addPendingRecompile(cx, script_);
     }
 };
 
 } /* anonymous namespace */
 
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
@@ -1846,17 +1936,17 @@ TypeCompartment::addPendingRecompile(JSC
     if (script->hasParallelIonScript())
         addPendingRecompile(cx, script->parallelIonScript()->recompileInfo());
 #endif
 
     // When one script is inlined into another the caller listens to state
     // changes on the callee's script, so trigger these to force recompilation
     // of any such callers.
     if (script->function() && !script->function()->hasLazyType())
-        ObjectStateChange(cx, script->function()->type(), false, true);
+        ObjectStateChange(cx, script->function()->type(), false);
 }
 
 void
 TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
 {
     JS_ASSERT(this == &cx->compartment()->types);
     JS_ASSERT(!(target->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
     JS_ASSERT(!target->singleton);
@@ -2533,17 +2623,17 @@ TypeObject::markStateChange(ExclusiveCon
         return;
 
     AutoEnterAnalysis enter(cxArg);
     TypeSet *types = maybeGetProperty(JSID_EMPTY);
     if (types) {
         if (JSContext *cx = cxArg->maybeJSContext()) {
             TypeConstraint *constraint = types->constraintList;
             while (constraint) {
-                constraint->newObjectState(cx, this, true);
+                constraint->newObjectState(cx, this);
                 constraint = constraint->next;
             }
         } else {
             JS_ASSERT(!types->constraintList);
         }
     }
 }
 
@@ -2560,33 +2650,33 @@ TypeObject::setFlags(ExclusiveContext *c
         JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED,
                      singleton->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON));
     }
 
     this->flags |= flags;
 
     InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags);
 
-    ObjectStateChange(cx, this, false, false);
+    ObjectStateChange(cx, this, false);
 }
 
 void
 TypeObject::markUnknown(ExclusiveContext *cx)
 {
     AutoEnterAnalysis enter(cx);
 
     JS_ASSERT(cx->compartment()->activeAnalysis);
     JS_ASSERT(!unknownProperties());
 
     if (!(flags & OBJECT_FLAG_ADDENDUM_CLEARED))
         clearAddendum(cx);
 
     InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this));
 
-    ObjectStateChange(cx, this, true, true);
+    ObjectStateChange(cx, this, true);
 
     /*
      * Existing constraints may have already been added to this object, which we need
      * to do the right thing for. We can't ensure that we will mark all unknown
      * objects before they have been accessed, as the __proto__ of a known object
      * could be dynamically set to an unknown object, and we can decide to ignore
      * properties of an object during analysis (i.e. hashmaps). Adding unknown for
      * any properties accessed already accounts for possible values read from them.
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -319,21 +319,21 @@ public:
 
     /*
      * For constraints attached to an object property's type set, mark the
      * property as having been configured.
      */
     virtual void newPropertyState(JSContext *cx, TypeSet *source) {}
 
     /*
-     * For constraints attached to the JSID_EMPTY type set on an object, mark a
-     * change in one of the object's dynamic property flags. If force is set,
-     * recompilation is always triggered.
+     * For constraints attached to the JSID_EMPTY type set on an object,
+     * indicate a change in one of the object's dynamic property flags or other
+     * state.
      */
-    virtual void newObjectState(JSContext *cx, TypeObject *object, bool force) {}
+    virtual void newObjectState(JSContext *cx, TypeObject *object) {}
 };
 
 /* Flags and other state stored in TypeSet::flags */
 enum {
     TYPE_FLAG_UNDEFINED =  0x1,
     TYPE_FLAG_NULL      =  0x2,
     TYPE_FLAG_BOOLEAN   =  0x4,
     TYPE_FLAG_INT32     =  0x8,
@@ -1251,23 +1251,26 @@ struct TypeObjectKey {
 
     const Class *clasp();
     TaggedProto proto();
     JSObject *singleton();
     TypeNewScript *newScript();
 
     bool unknownProperties();
     bool hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags);
-    void watchStateChange(CompilerConstraintList *constraints);
+    void watchStateChangeForInlinedCall(CompilerConstraintList *constraints);
+    void watchStateChangeForNewScriptTemplate(CompilerConstraintList *constraints);
+    void watchStateChangeForTypedArrayBuffer(CompilerConstraintList *constraints);
     HeapTypeSetKey property(jsid id);
 };
 
 class HeapTypeSetKey
 {
   public:
+    TypeObject *actualObject;
     HeapTypeSet *actualTypes;
 
     void freeze(CompilerConstraintList *constraints);
     JSValueType knownTypeTag(CompilerConstraintList *constraints);
     bool configured(CompilerConstraintList *constraints, TypeObjectKey *type);
     bool notEmpty(CompilerConstraintList *constraints);
     bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other);
     JSObject *singleton(CompilerConstraintList *constraints);