[INFER] Encapsulate GC arena/cell iteration, bug 679887.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 26 Aug 2011 12:39:31 -0700
changeset 76153 907c553b698f26dbc36eafe25216e579e9f6e9d4
parent 76152 65562c596db33e91274748eb76e9686de63a29bc
child 76154 53e56c928eaddff935c672804606fb426a07acf6
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
bugs679887
milestone9.0a1
[INFER] Encapsulate GC arena/cell iteration, bug 679887.
js/src/jscompartment.cpp
js/src/jsgc.cpp
js/src/jsgcinlines.h
js/src/jsgcmark.cpp
js/src/jsinfer.cpp
js/src/jsobj.h
js/src/methodjit/BaseAssembler.h
js/src/methodjit/Compiler.cpp
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -481,66 +481,61 @@ void
 JSCompartment::markCrossCompartmentWrappers(JSTracer *trc)
 {
     JS_ASSERT(trc->context->runtime->gcCurrentCompartment);
 
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront())
         MarkValue(trc, e.front().key, "cross-compartment wrapper");
 }
 
+struct MarkSingletonObjectOp
+{
+    JSTracer *trc;
+    MarkSingletonObjectOp(JSTracer *trc) : trc(trc) {}
+    void operator()(Cell *cell) {
+        JSObject *object = static_cast<JSObject *>(cell);
+        if (!object->isNewborn() && object->hasSingletonType())
+            MarkObject(trc, *object, "mark_types_singleton");
+    }
+};
+
+struct MarkTypeObjectOp
+{
+    JSTracer *trc;
+    MarkTypeObjectOp(JSTracer *trc) : trc(trc) {}
+    void operator()(Cell *cell) {
+        types::TypeObject *object = static_cast<types::TypeObject *>(cell);
+        MarkTypeObject(trc, object, "mark_types_scan");
+    }
+};
+
 void
 JSCompartment::markTypes(JSTracer *trc)
 {
     /*
      * Mark all scripts, type objects and singleton JS objects in the
      * compartment. These can be referred to directly by type sets, which we
      * cannot modify while code which depends on these type sets is active.
      */
     JS_ASSERT(activeAnalysis);
 
     for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
         JSScript *script = reinterpret_cast<JSScript *>(cursor);
         js_TraceScript(trc, script, NULL);
     }
 
-    JS_STATIC_ASSERT(FINALIZE_FUNCTION_AND_OBJECT_LAST < FINALIZE_TYPE_OBJECT);
-
+    MarkSingletonObjectOp objectCellOp(trc);
     for (unsigned thingKind = FINALIZE_OBJECT0;
-         thingKind < FINALIZE_TYPE_OBJECT;
+         thingKind <= FINALIZE_FUNCTION_AND_OBJECT_LAST;
          thingKind++) {
-        bool isObjectKind = thingKind <= FINALIZE_FUNCTION_AND_OBJECT_LAST;
-        if (!isObjectKind)
-            thingKind = FINALIZE_TYPE_OBJECT;
-
-        size_t thingSize = GCThingSizeMap[thingKind];
-        ArenaHeader *aheader = arenas[thingKind].getHead();
-
-        for (; aheader; aheader = aheader->next) {
-            Arena *arena = aheader->getArena();
-            FreeSpan firstSpan(aheader->getFirstFreeSpan());
-            const FreeSpan *span = &firstSpan;
+        gc::ForEachArenaAndCell(this, (FinalizeKind) thingKind, EmptyArenaOp, objectCellOp);
+    }
 
-            for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) {
-                JS_ASSERT(thing <= arena->thingsEnd());
-                if (thing == span->first) {
-                    if (!span->hasNext())
-                        break;
-                    thing = span->last;
-                    span = span->nextSpan();
-                } else if (isObjectKind) {
-                    JSObject *object = reinterpret_cast<JSObject *>(thing);
-                    if (object->lastProp && object->hasSingletonType())
-                        MarkObject(trc, *object, "mark_types_singleton");
-                } else {
-                    types::TypeObject *object = reinterpret_cast<types::TypeObject *>(thing);
-                    MarkTypeObject(trc, object, "object_root");
-                }
-            }
-        }
-    }
+    MarkTypeObjectOp typeCellOp(trc);
+    gc::ForEachArenaAndCell(this, FINALIZE_TYPE_OBJECT, EmptyArenaOp, typeCellOp);
 }
 
 void
 JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
 {
     /* Remove dead wrappers from the table. */
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
         JS_ASSERT_IF(IsAboutToBeFinalized(cx, e.front().key.toGCThing()) &&
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2837,16 +2837,44 @@ TraceRuntime(JSTracer *trc)
 
     /*
      * Calls from inside a normal GC or a recursive calls are OK and do not
      * require session setup.
      */
     MarkRuntime(trc);
 }
 
+struct IterateArenaCallbackOp
+{
+    JSContext *cx;
+    void *data;
+    IterateArenaCallback callback;
+    size_t traceKind;
+    size_t thingSize;
+    IterateArenaCallbackOp(JSContext *cx, void *data, IterateArenaCallback callback,
+                           size_t traceKind, size_t thingSize)
+        : cx(cx), data(data), callback(callback), traceKind(traceKind), thingSize(thingSize)
+    {}
+    void operator()(Arena *arena) { (*callback)(cx, data, arena, traceKind, thingSize); }
+};
+
+struct IterateCellCallbackOp
+{
+    JSContext *cx;
+    void *data;
+    IterateCellCallback callback;
+    size_t traceKind;
+    size_t thingSize;
+    IterateCellCallbackOp(JSContext *cx, void *data, IterateCellCallback callback,
+                          size_t traceKind, size_t thingSize)
+        : cx(cx), data(data), callback(callback), traceKind(traceKind), thingSize(thingSize)
+    {}
+    void operator()(Cell *cell) { (*callback)(cx, data, cell, traceKind, thingSize); }
+};
+
 void
 IterateCompartmentsArenasCells(JSContext *cx, void *data,
                                IterateCompartmentCallback compartmentCallback,
                                IterateArenaCallback arenaCallback,
                                IterateCellCallback cellCallback)
 {
     CHECK_REQUEST(cx);
     LeaveTrace(cx);
@@ -2864,37 +2892,20 @@ IterateCompartmentsArenasCells(JSContext
     AutoCopyFreeListToArenas copy(rt);
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
         JSCompartment *compartment = *c;
         (*compartmentCallback)(cx, data, compartment);
 
         for (unsigned thingKind = 0; thingKind < FINALIZE_LIMIT; thingKind++) {
             size_t traceKind = GetFinalizableTraceKind(thingKind);
             size_t thingSize = GCThingSizeMap[thingKind];
-            ArenaHeader *aheader = compartment->arenas[thingKind].getHead();
-
-            for (; aheader; aheader = aheader->next) {
-                Arena *arena = aheader->getArena();
-                (*arenaCallback)(cx, data, arena, traceKind, thingSize);
-                FreeSpan firstSpan(aheader->getFirstFreeSpan());
-                const FreeSpan *span = &firstSpan;
-
-                for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) {
-                    JS_ASSERT(thing <= arena->thingsEnd());
-                    if (thing == span->first) {
-                        if (!span->hasNext())
-                            break;
-                        thing = span->last;
-                        span = span->nextSpan();
-                    } else {
-                        void *t = reinterpret_cast<void *>(thing);
-                        (*cellCallback)(cx, data, t, traceKind, thingSize);
-                    }
-                }
-            }
+            IterateArenaCallbackOp arenaOp(cx, data, arenaCallback, traceKind, thingSize);
+            IterateCellCallbackOp cellOp(cx, data, cellCallback, traceKind, thingSize);
+
+            ForEachArenaAndCell(compartment, (FinalizeKind) thingKind, arenaOp, cellOp);
         }
     }
 }
 
 void
 IterateCells(JSContext *cx, JSCompartment *compartment, FinalizeKind thingKind,
              void *data, IterateCellCallback cellCallback)
 {
@@ -2912,36 +2923,19 @@ IterateCells(JSContext *cx, JSCompartmen
     rt->gcHelperThread.waitBackgroundSweepEnd(rt, false);
 #endif
     AutoUnlockGC unlock(rt);
 
     AutoCopyFreeListToArenas copy(rt);
 
     size_t traceKind = GetFinalizableTraceKind(thingKind);
     size_t thingSize = GCThingSizeMap[thingKind];
-    ArenaHeader *aheader = compartment->arenas[thingKind].getHead();
-
-    for (; aheader; aheader = aheader->next) {
-        Arena *arena = aheader->getArena();
-        FreeSpan firstSpan(aheader->getFirstFreeSpan());
-        const FreeSpan *span = &firstSpan;
-
-        for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) {
-            JS_ASSERT(thing <= arena->thingsEnd());
-            if (thing == span->first) {
-                if (!span->hasNext())
-                    break;
-                thing = span->last;
-                span = span->nextSpan();
-            } else {
-                (*cellCallback)(cx, data, reinterpret_cast<void *>(thing), traceKind,
-                                thingSize);
-            }
-        }
-    }
+    IterateCellCallbackOp cellOp(cx, data, cellCallback, traceKind, thingSize);
+
+    ForEachArenaAndCell(compartment, thingKind, EmptyArenaOp, cellOp);
 }
 
 namespace gc {
 
 JSCompartment *
 NewCompartment(JSContext *cx, JSPrincipals *principals)
 {
     JSRuntime *rt = cx->runtime;
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -218,16 +218,54 @@ GCPoke(JSContext *cx, Value oldval)
 
 #ifdef JS_GC_ZEAL
     /* Schedule a GC to happen "soon" after a GC poke. */
     if (cx->runtime->gcZeal())
         cx->runtime->gcNextScheduled = 1;
 #endif
 }
 
+/*
+ * Invoke ArenaOp and CellOp on every arena and cell in a compartment which
+ * have the specified thing kind.
+ */
+template <class ArenaOp, class CellOp>
+void
+ForEachArenaAndCell(JSCompartment *compartment, FinalizeKind thingKind,
+                    ArenaOp arenaOp, CellOp cellOp)
+{
+    size_t thingSize = GCThingSizeMap[thingKind];
+    ArenaHeader *aheader = compartment->arenas[thingKind].getHead();
+
+    for (; aheader; aheader = aheader->next) {
+        Arena *arena = aheader->getArena();
+        arenaOp(arena);
+        FreeSpan firstSpan(aheader->getFirstFreeSpan());
+        const FreeSpan *span = &firstSpan;
+
+        for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) {
+            JS_ASSERT(thing <= arena->thingsEnd());
+            if (thing == span->first) {
+                if (!span->hasNext())
+                    break;
+                thing = span->last;
+                span = span->nextSpan();
+            } else {
+                Cell *t = reinterpret_cast<Cell *>(thing);
+                cellOp(t);
+            }
+        }
+    }
+}
+
+/* Signatures for ArenaOp and CellOp above. */
+
+inline void EmptyArenaOp(Arena *arena) {}
+inline void EmptyCellOp(Cell *t) {}
+
 } /* namespace gc */
 } /* namespace js */
 
 /*
  * Allocates a new GC thing. After a successful allocation the caller must
  * fully initialize the thing before calling any function that can potentially
  * trigger GC. This will ensure that GC tracing never sees junk values stored
  * in the partially initialized thing.
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -208,18 +208,23 @@ MarkTypeObject(JSTracer *trc, types::Typ
 {
     JS_ASSERT(trc);
     JS_ASSERT(type);
     JS_SET_TRACING_NAME(trc, name);
     if (type == &types::emptyTypeObject)
         return;
     Mark(trc, type);
 
+    /*
+     * Mark parts of a type object skipped by ScanTypeObject. ScanTypeObject is
+     * only used for marking tracers; for tracers with a callback, if we
+     * reenter through JS_TraceChildren then MarkChildren will *not* skip these
+     * members, and we don't need to handle them here.
+     */
     if (IS_GC_MARKING_TRACER(trc)) {
-        /* Mark parts of a type object skipped by ScanTypeObject. */
         if (type->singleton)
             MarkObject(trc, *type->singleton, "type_singleton");
         if (type->functionScript)
             js_TraceScript(trc, type->functionScript, NULL);
     }
 }
 
 #if JS_HAS_XML_SUPPORT
@@ -656,17 +661,17 @@ PushMarkStack(GCMarker *gcmarker, JSStri
 static const uintN LARGE_OBJECT_CHUNK_SIZE = 2048;
 
 static void
 ScanObject(GCMarker *gcmarker, JSObject *obj)
 {
     if (obj->isNewborn())
         return;
 
-    types::TypeObject *type = obj->gctype();
+    types::TypeObject *type = obj->typeFromGC();
     if (type != &types::emptyTypeObject)
         PushMarkStack(gcmarker, type);
 
     if (JSObject *parent = obj->getParent())
         PushMarkStack(gcmarker, parent);
 
     /*
      * Call the trace hook if necessary, and check for a newType on objects
@@ -746,17 +751,17 @@ ScanLargeObject(GCMarker *gcmarker, Larg
 
 void
 MarkChildren(JSTracer *trc, JSObject *obj)
 {
     /* If obj has no map, it must be a newborn. */
     if (obj->isNewborn())
         return;
 
-    MarkTypeObject(trc, obj->gctype(), "type");
+    MarkTypeObject(trc, obj->typeFromGC(), "type");
 
     /* Trace universal (ops-independent) members. */
     if (!obj->isDenseArray() && obj->newType)
         MarkTypeObject(trc, obj->newType, "new_type");
     if (JSObject *parent = obj->getParent())
         MarkObject(trc, *parent, "parent");
 
     Class *clasp = obj->getClass();
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -5409,42 +5409,34 @@ TypeObject::sweep(JSContext *cx)
      * The GC will clear out the constraints ensuring the correctness of the
      * newScript information, these constraints will need to be regenerated
      * the next time we compile code which depends on this info.
      */
     if (newScript)
         flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
 }
 
+struct SweepTypeObjectOp
+{
+    JSContext *cx;
+    SweepTypeObjectOp(JSContext *cx) : cx(cx) {}
+    void operator()(gc::Cell *cell) {
+        TypeObject *object = static_cast<TypeObject *>(cell);
+        object->sweep(cx);
+    }
+};
+
 void
 SweepTypeObjects(JSContext *cx, JSCompartment *compartment)
 {
     JS_ASSERT(!emptyTypeObject.emptyShapes);
     JS_ASSERT(!emptyTypeObject.newScript);
 
-    gc::ArenaHeader *aheader = compartment->arenas[gc::FINALIZE_TYPE_OBJECT].getHead();
-    size_t thingSize = sizeof(TypeObject);
-
-    for (; aheader; aheader = aheader->next) {
-        gc::Arena *arena = aheader->getArena();
-        gc::FreeSpan firstSpan(aheader->getFirstFreeSpan());
-        const gc::FreeSpan *span = &firstSpan;
-
-        for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) {
-            JS_ASSERT(thing <= arena->thingsEnd());
-            if (thing == span->first) {
-                if (!span->hasNext())
-                    break;
-                thing = span->last;
-                span = span->nextSpan();
-            } else {
-                reinterpret_cast<TypeObject *>(thing)->sweep(cx);
-            }
-        }
-    }
+    SweepTypeObjectOp op(cx);
+    gc::ForEachArenaAndCell(compartment, gc::FINALIZE_TYPE_OBJECT, gc::EmptyArenaOp, op);
 }
 
 void
 TypeCompartment::sweep(JSContext *cx)
 {
     JSCompartment *compartment = this->compartment();
 
     SweepTypeObjects(cx, compartment);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -840,17 +840,17 @@ struct JSObject : js::gc::Cell {
 
     inline js::types::TypeObject *getType(JSContext *cx);
 
     js::types::TypeObject *type() const {
         JS_ASSERT(!hasLazyType());
         return type_;
     }
 
-    js::types::TypeObject *gctype() const {
+    js::types::TypeObject *typeFromGC() const {
         /* Direct field access for use by GC. */
         return type_;
     }
 
     static inline size_t offsetOfType() { return offsetof(JSObject, type_); }
 
     inline void clearType();
     inline void setType(js::types::TypeObject *newType);
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -1290,17 +1290,17 @@ static const JSC::MacroAssembler::Regist
         storePtr(ImmPtr(templateObject->lastProp), Address(result, offsetof(JSObject, lastProp)));
         storePtr(ImmPtr(templateObject->clasp), Address(result, offsetof(JSObject, clasp)));
         store32(Imm32(templateObject->flags), Address(result, offsetof(JSObject, flags)));
         store32(Imm32(templateObject->objShape), Address(result, offsetof(JSObject, objShape)));
         storePtr(ImmPtr(templateObject->newType), Address(result, offsetof(JSObject, newType)));
         storePtr(ImmPtr(templateObject->parent), Address(result, offsetof(JSObject, parent)));
         storePtr(ImmPtr(templateObject->privateData), Address(result, offsetof(JSObject, privateData)));
         storePtr(ImmPtr((void *) templateObject->capacity), Address(result, offsetof(JSObject, capacity)));
-        storePtr(ImmPtr(templateObject->gctype()), Address(result, JSObject::offsetOfType()));
+        storePtr(ImmPtr(templateObject->type()), Address(result, JSObject::offsetOfType()));
 
         /* Fixed slots of non-array objects are required to be initialized. */
         if (!templateObject->isDenseArray()) {
             for (unsigned i = 0; i < templateObject->numFixedSlots(); i++)
                 storeValue(UndefinedValue(), Address(result, JSObject::getFixedSlotOffset(i)));
         }
 
         return jump;
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -7235,18 +7235,18 @@ mjit::Compiler::addTypeTest(types::TypeS
         count = types->getObjectCount();
 
     if (count != 0) {
         Jump notObject = masm.testObject(Assembler::NotEqual, typeReg);
         Address typeAddress(dataReg, JSObject::offsetOfType());
 
         /*
          * Test for a singleton objects first. If singletons have lazy types
-         * then they may share their gctype() with another type object in the
-         * observed set and we can get a spurious match.
+         * then they may share their raw type pointer with another type object
+         * in the observed set and we can get a spurious match.
          */
         Jump notSingleton = masm.branchTest32(Assembler::Zero,
                                               Address(dataReg, offsetof(JSObject, flags)),
                                               Imm32(JSObject::SINGLETON_TYPE));
 
         for (unsigned i = 0; i < count; i++) {
             if (JSObject *object = types->getSingleObject(i))
                 matches.append(masm.branchPtr(Assembler::Equal, dataReg, ImmPtr(object)));