Bug 1237445 - Use GCHashTables for ObjectGroup sweeping, r=terrence
authorSteve Fink <sfink@mozilla.com>
Wed, 06 Jan 2016 18:12:23 -0800
changeset 279065 5d93baf4ff9c536b15818ac839e63b42964e7ead
parent 279064 28657e5774d031406f53fd4f5db9bbe45eb47474
child 279066 749795f067db4010183503c85cd5a3d376fa0a89
push id29867
push usercbook@mozilla.com
push dateFri, 08 Jan 2016 11:00:43 +0000
treeherdermozilla-central@1424cdfc075d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1237445
milestone46.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 1237445 - Use GCHashTables for ObjectGroup sweeping, r=terrence
js/src/vm/ObjectGroup.cpp
js/src/vm/ObjectGroup.h
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/ObjectGroup.h"
 
 #include "jshashutil.h"
 #include "jsobj.h"
 
+#include "gc/Marking.h"
 #include "gc/StoreBuffer.h"
 #include "gc/Zone.h"
 #include "vm/ArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/UnboxedObject-inl.h"
@@ -400,16 +401,21 @@ struct ObjectGroupCompartment::NewEntry
 
     static inline bool match(const NewEntry& key, const Lookup& lookup) {
         return key.group.unbarrieredGet()->proto() == lookup.matchProto &&
                (!lookup.clasp || key.group.unbarrieredGet()->clasp() == lookup.clasp) &&
                key.associated == lookup.associated;
     }
 
     static void rekey(NewEntry& k, const NewEntry& newKey) { k = newKey; }
+
+    bool needsSweep() {
+        return (IsAboutToBeFinalized(&group) ||
+                (associated && IsAboutToBeFinalizedUnbarriered(&associated)));
+    }
 };
 
 // This class is used to add a post barrier on a NewTable entry, as the key is
 // calculated from a prototype object which may be moved by generational GC.
 class ObjectGroupCompartment::NewTableRef : public gc::BufferableRef
 {
     NewTable* table;
     const Class* clasp;
@@ -753,16 +759,28 @@ struct ObjectGroupCompartment::ArrayObje
 
     bool operator==(const ArrayObjectKey& other) {
         return type == other.type;
     }
 
     bool operator!=(const ArrayObjectKey& other) {
         return !(*this == other);
     }
+
+    bool needsSweep() {
+        MOZ_ASSERT(type.isUnknown() || !type.isSingleton());
+        if (!type.isUnknown() && type.isGroup()) {
+            ObjectGroup* group = type.groupNoBarrier();
+            if (IsAboutToBeFinalizedUnbarriered(&group))
+                return true;
+            if (group != type.groupNoBarrier())
+                type = TypeSet::ObjectType(group);
+        }
+        return false;
+    }
 };
 
 static inline bool
 NumberTypes(TypeSet::Type a, TypeSet::Type b)
 {
     return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE))
         && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE));
 }
@@ -1124,23 +1142,49 @@ struct ObjectGroupCompartment::PlainObje
         if (lookup.nproperties != v.nproperties)
             return false;
         for (size_t i = 0; i < lookup.nproperties; i++) {
             if (lookup.properties[i].id != v.properties[i])
                 return false;
         }
         return true;
     }
+
+    bool needsSweep() {
+        for (unsigned i = 0; i < nproperties; i++) {
+            if (gc::IsAboutToBeFinalizedUnbarriered(&properties[i]))
+                return true;
+        }
+        return false;
+    }
 };
 
 struct ObjectGroupCompartment::PlainObjectEntry
 {
     ReadBarrieredObjectGroup group;
     ReadBarrieredShape shape;
     TypeSet::Type* types;
+
+    bool needsSweep(unsigned nproperties) {
+        if (IsAboutToBeFinalized(&group))
+            return true;
+        if (IsAboutToBeFinalized(&shape))
+            return true;
+        for (unsigned i = 0; i < nproperties; i++) {
+            MOZ_ASSERT(!types[i].isSingleton());
+            if (types[i].isGroup()) {
+                ObjectGroup* group = types[i].groupNoBarrier();
+                if (IsAboutToBeFinalizedUnbarriered(&group))
+                    return true;
+                if (group != types[i].groupNoBarrier())
+                    types[i] = TypeSet::ObjectType(group);
+            }
+        }
+        return false;
+    }
 };
 
 static bool
 CanShareObjectGroup(IdValuePair* properties, size_t nproperties)
 {
     // Don't reuse groups for objects containing indexed properties, which
     // might end up as dense elements.
     for (size_t i = 0; i < nproperties; i++) {
@@ -1387,16 +1431,21 @@ struct ObjectGroupCompartment::Allocatio
                a.kind == b.kind &&
                MovableCellHasher<JSObject*>::match(a.proto, b.proto);
     }
 
     static void trace(AllocationSiteKey* key, JSTracer* trc) {
         TraceRoot(trc, &key->script, "AllocationSiteKey script");
         TraceNullableRoot(trc, &key->proto, "AllocationSiteKey proto");
     }
+
+    bool needsSweep() {
+        return IsAboutToBeFinalizedUnbarriered(script.unsafeGet()) ||
+            (proto && IsAboutToBeFinalizedUnbarriered(proto.unsafeGet()));
+    }
 };
 
 /* static */ ObjectGroup*
 ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode* pc,
                                  JSProtoKey kind, HandleObject protoArg /* = nullptr */)
 {
     MOZ_ASSERT(!useSingletonForAllocationSite(scriptArg, pc, kind));
     MOZ_ASSERT_IF(protoArg, kind == JSProto_Array);
@@ -1717,110 +1766,45 @@ ObjectGroupCompartment::clearTables()
         plainObjectTable->clear();
     }
     if (defaultNewTable && defaultNewTable->initialized())
         defaultNewTable->clear();
     if (lazyTable && lazyTable->initialized())
         lazyTable->clear();
 }
 
+/* static */ bool
+ObjectGroupCompartment::PlainObjectGCPolicy::needsSweep(PlainObjectKey* key,
+                                                        PlainObjectEntry* entry)
+{
+    if (!(KeyPolicy::needsSweep(key) || entry->needsSweep(key->nproperties)))
+        return false;
+    js_free(key->properties);
+    js_free(entry->types);
+    return true;
+}
+
 void
 ObjectGroupCompartment::sweep(FreeOp* fop)
 {
     /*
      * Iterate through the array/object group tables and remove all entries
      * referencing collected data. These tables only hold weak references.
      */
 
-    if (arrayObjectTable) {
-        for (ArrayObjectTable::Enum e(*arrayObjectTable); !e.empty(); e.popFront()) {
-            ArrayObjectKey key = e.front().key();
-            MOZ_ASSERT(key.type.isUnknown() || !key.type.isSingleton());
-
-            bool remove = false;
-            if (!key.type.isUnknown() && key.type.isGroup()) {
-                ObjectGroup* group = key.type.groupNoBarrier();
-                if (IsAboutToBeFinalizedUnbarriered(&group))
-                    remove = true;
-                else
-                    key.type = TypeSet::ObjectType(group);
-            }
-            if (IsAboutToBeFinalized(&e.front().value()))
-                remove = true;
-
-            if (remove)
-                e.removeFront();
-            else if (key != e.front().key())
-                e.rekeyFront(key);
-        }
-    }
-
-    if (plainObjectTable) {
-        for (PlainObjectTable::Enum e(*plainObjectTable); !e.empty(); e.popFront()) {
-            const PlainObjectKey& key = e.front().key();
-            PlainObjectEntry& entry = e.front().value();
-
-            bool remove = false;
-            if (IsAboutToBeFinalized(&entry.group))
-                remove = true;
-            if (IsAboutToBeFinalized(&entry.shape))
-                remove = true;
-            for (unsigned i = 0; !remove && i < key.nproperties; i++) {
-                if (gc::IsAboutToBeFinalizedUnbarriered(&key.properties[i]))
-                    remove = true;
-
-                MOZ_ASSERT(!entry.types[i].isSingleton());
-                if (entry.types[i].isGroup()) {
-                    ObjectGroup* group = entry.types[i].groupNoBarrier();
-                    if (IsAboutToBeFinalizedUnbarriered(&group))
-                        remove = true;
-                    else if (group != entry.types[i].groupNoBarrier())
-                        entry.types[i] = TypeSet::ObjectType(group);
-                }
-            }
-
-            if (remove) {
-                js_free(key.properties);
-                js_free(entry.types);
-                e.removeFront();
-            }
-        }
-    }
-
-    if (allocationSiteTable) {
-        for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) {
-            bool keyDying = IsAboutToBeFinalized(&e.front().mutableKey().script) ||
-                            (e.front().key().proto && IsAboutToBeFinalized(&e.front().mutableKey().proto));
-            bool valDying = IsAboutToBeFinalized(&e.front().value());
-            if (keyDying || valDying)
-                e.removeFront();
-        }
-    }
-
-    sweepNewTable(defaultNewTable);
-    sweepNewTable(lazyTable);
-}
-
-void
-ObjectGroupCompartment::sweepNewTable(NewTable* table)
-{
-    if (table && table->initialized()) {
-        for (NewTable::Enum e(*table); !e.empty(); e.popFront()) {
-            NewEntry entry = e.front();
-            if (IsAboutToBeFinalized(&entry.group) ||
-                (entry.associated && IsAboutToBeFinalizedUnbarriered(&entry.associated)))
-            {
-                e.removeFront();
-            } else {
-                /* Any rekeying necessary is handled by fixupNewObjectGroupTable() below. */
-                MOZ_ASSERT(entry.group.unbarrieredGet() == e.front().group.unbarrieredGet());
-                MOZ_ASSERT(entry.associated == e.front().associated);
-            }
-        }
-    }
+    if (arrayObjectTable)
+        arrayObjectTable->sweep();
+    if (plainObjectTable)
+        plainObjectTable->sweep();
+    if (allocationSiteTable)
+        allocationSiteTable->sweep();
+    if (defaultNewTable)
+        defaultNewTable->sweep();
+    if (lazyTable)
+        lazyTable->sweep();
 }
 
 void
 ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable* table)
 {
     /*
      * Each entry's hash depends on the object's prototype and we can't tell
      * whether that has been moved or not in sweepNewObjectGroupTable().
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -534,53 +534,57 @@ class ObjectGroup : public gc::TenuredCe
 };
 
 // Structure used to manage the groups in a compartment.
 class ObjectGroupCompartment
 {
     friend class ObjectGroup;
 
     struct NewEntry;
-    typedef HashSet<NewEntry, NewEntry, SystemAllocPolicy> NewTable;
+    using NewTable = js::GCHashSet<NewEntry, NewEntry, SystemAllocPolicy>;
     class NewTableRef;
 
     // Set of default 'new' or lazy groups in the compartment.
     NewTable* defaultNewTable;
     NewTable* lazyTable;
 
     struct ArrayObjectKey;
-    typedef HashMap<ArrayObjectKey,
-                    ReadBarrieredObjectGroup,
-                    ArrayObjectKey,
-                    SystemAllocPolicy> ArrayObjectTable;
+    using ArrayObjectTable = js::GCHashMap<ArrayObjectKey,
+                                           ReadBarrieredObjectGroup,
+                                           ArrayObjectKey,
+                                           SystemAllocPolicy>;
 
     struct PlainObjectKey;
     struct PlainObjectEntry;
-    typedef HashMap<PlainObjectKey,
-                    PlainObjectEntry,
-                    PlainObjectKey,
-                    SystemAllocPolicy> PlainObjectTable;
+    struct PlainObjectGCPolicy : public DefaultMapGCPolicy<PlainObjectKey, PlainObjectEntry> {
+        static bool needsSweep(PlainObjectKey* key, PlainObjectEntry* entry);
+    };
+    using PlainObjectTable = js::GCHashMap<PlainObjectKey,
+                                           PlainObjectEntry,
+                                           PlainObjectKey,
+                                           SystemAllocPolicy,
+                                           PlainObjectGCPolicy>;
 
     // Tables for managing groups common to the contents of large script
     // singleton objects and JSON objects. These are vanilla ArrayObjects and
     // PlainObjects, so we distinguish the groups of different ones by looking
     // at the types of their properties.
     //
     // All singleton/JSON arrays which have the same prototype, are homogenous
     // and of the same element type will share a group. All singleton/JSON
     // objects which have the same shape and property types will also share a
     // group. We don't try to collate arrays or objects with type mismatches.
     ArrayObjectTable* arrayObjectTable;
     PlainObjectTable* plainObjectTable;
 
     struct AllocationSiteKey;
-    typedef HashMap<AllocationSiteKey,
-                    ReadBarrieredObjectGroup,
-                    AllocationSiteKey,
-                    SystemAllocPolicy> AllocationSiteTable;
+    using AllocationSiteTable = js::GCHashMap<AllocationSiteKey,
+                                              ReadBarrieredObjectGroup,
+                                              AllocationSiteKey,
+                                              SystemAllocPolicy>;
 
     // Table for referencing types of objects keyed to an allocation site.
     AllocationSiteTable* allocationSiteTable;
 
   public:
     ObjectGroupCompartment();
     ~ObjectGroupCompartment();
 
@@ -617,17 +621,16 @@ class ObjectGroupCompartment
         fixupNewTableAfterMovingGC(lazyTable);
     }
 
   private:
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkNewTableAfterMovingGC(NewTable* table);
 #endif
 
-    void sweepNewTable(NewTable* table);
     void fixupNewTableAfterMovingGC(NewTable* table);
 
     static void newTablePostBarrier(ExclusiveContext* cx, NewTable* table,
                                     const Class* clasp, TaggedProto proto, JSObject* associated);
 };
 
 PlainObject*
 NewPlainObjectWithProperties(ExclusiveContext* cx, IdValuePair* properties, size_t nproperties,