Use the card marks to find gray objects during a concurrent collection.

Presently, the garbage collector scans the mark bits looking for gray
objects.  As of this change, only objects spanning dirty cards will be
reexamined during re-marking.

As part of this change, re-marking of roots will push objects onto the
mark stack instead of setting their mark bits.  The number of gray
roots discovered during re-marking is small.  If this changes we can
dirty the cards instead and let re-scanning push the gray objects.

Change-Id: If270812821e070d09af344edb63dfede26d10410
diff --git a/vm/alloc/CardTable.c b/vm/alloc/CardTable.c
index 45fc1d4..bd8de9a 100644
--- a/vm/alloc/CardTable.c
+++ b/vm/alloc/CardTable.c
@@ -38,21 +38,6 @@
  * ObjectInlines.h [such as dvmSetFieldObject] do this for you. The
  * JIT and fast interpreters also contain code to mark cards as dirty.
  *
- * [TODO: Concurrent collection will have to expand on this, as it
- * uses the card table as well.]
- *
- * The card table is used to support partial collection, which at the
- * moment means "treat the zygote's heap as permanent, and only GC
- * objects in the application heap". In order to do this efficiently,
- * the GC need to find quickly references to objects in the
- * application heap from the zygote heap.  When an application creates
- * an object and stores it into an object on the zygote heap, it will
- * mark the corresponding card in the zygote heap as "dirty". When the
- * GC does a partial collection, it can efficiently find all the
- * cross-heap objects, since they are all on dirty cards. The GC also
- * takes the opportunity to mark as "clean" any cards which are dirty,
- * but no longer contain cross-heap pointers.
- *
  * The card table's base [the "biased card table"] gets set to a
  * rather strange value.  In order to keep the JIT from having to
  * fabricate or load GC_DIRTY_CARD to store into the card table,
@@ -109,6 +94,12 @@
     munmap(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength);
 }
 
+void dvmClearCardTable(void)
+{
+    assert(gDvm.gcHeap->cardTableBase != NULL);
+    memset(gDvm.gcHeap->cardTableBase, GC_CARD_CLEAN, gDvm.gcHeap->cardTableLength);
+}
+
 /*
  * Returns true iff the address is within the bounds of the card table.
  */
@@ -149,92 +140,3 @@
     u1 *cardAddr = dvmCardFromAddr(addr);
     *cardAddr = GC_CARD_DIRTY;
 }
-
-/*
- * Returns true iff all address within the Object are on unmarked cards.
- */
-static bool objectIsClean(const Object *obj)
-{
-    assert(dvmIsValidObject(obj));
-    size_t size = dvmHeapSourceChunkSize(obj);
-    u1 *start = dvmCardFromAddr(obj);
-    u1 *end = dvmCardFromAddr((char *)obj + size-1);
-    u1 *index;
-
-    for (index = start; index <= end; index++) {
-        if (*index != GC_CARD_CLEAN) {
-            return false;
-        }
-    }
-    return true;
-}
-
-/*
- * A Visitor callback in support of checkCleanObjects. "arg" is
- * expected to be the immuneLimit.
- */
-static void crossGenCheckVisitor(void *ptr, void *arg)
-{
-    Object *ref = *(Object **)ptr;
-    Object *immuneLimit = (Object *)arg;
-
-    if (ref >= immuneLimit) {
-        LOGE("Clean obj contains threatened ref %p: %p", ptr, ref);
-        dvmAbort();
-    }
-}
-
-/*
- * A HeapBitmap callback in support of checkCleanObjects.
- */
-static bool crossGenCheckCallback(size_t numPtrs, void **ptrs,
-                      const void *finger, void *arg)
-{
-    size_t i;
-    for (i = 0; i < numPtrs; i++) {
-        Object *obj = ptrs[i];
-        if (objectIsClean(obj)) {
-            dvmVisitObject(crossGenCheckVisitor, obj, arg);
-        }
-    }
-
-    return true;
-}
-
-/*
- * dvmAbort if any clean object in the Zygote heap contains a
- * reference to the application heap, or if the immune limit is not as
- * expected.
- */
-void dvmVerifyCardTable(void)
-{
-    HeapBitmap markBits[HEAP_SOURCE_MAX_HEAP_COUNT];
-    HeapBitmap liveBits[HEAP_SOURCE_MAX_HEAP_COUNT];
-    size_t numBitmaps;
-    void *immuneLimit;
-    numBitmaps = dvmHeapSourceGetNumHeaps();
-    if (numBitmaps < 2) {
-        return;
-    }
-    if (numBitmaps > 2) {
-        LOGE("More heaps than expected.");
-        dvmAbort();
-    }
-    immuneLimit = dvmHeapSourceGetImmuneLimit(GC_PARTIAL);
-    dvmHeapSourceGetObjectBitmaps(liveBits, markBits, numBitmaps);
-    if (markBits[0].base <= markBits[1].base) {
-        LOGE("Heaps are not in the expected order.");
-        dvmAbort();
-    }
-    if (markBits[0].base < (uintptr_t)immuneLimit ||
-        markBits[0].max < (uintptr_t)immuneLimit) {
-        LOGE("Application heap should not be immune.");
-        dvmAbort();
-    }
-    if (markBits[1].base >= (uintptr_t)immuneLimit ||
-        markBits[1].max >= (uintptr_t)immuneLimit) {
-        LOGE("Zygote heap should not be threatened.");
-        dvmAbort();
-    }
-    dvmHeapBitmapWalk(&markBits[1], crossGenCheckCallback, immuneLimit);
-}
diff --git a/vm/alloc/CardTable.h b/vm/alloc/CardTable.h
index 4c4bee6..7a86bd0 100644
--- a/vm/alloc/CardTable.h
+++ b/vm/alloc/CardTable.h
@@ -44,6 +44,11 @@
 void dvmCardTableShutdown(void);
 
 /*
+ * Resets all of the bytes in the card table to clean.
+ */
+void dvmClearCardTable(void);
+
+/*
  * Returns the address of the relevent byte in the card table, given
  * an address on the heap.
  */
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
index 02eb920..f063b45 100644
--- a/vm/alloc/Heap.c
+++ b/vm/alloc/Heap.c
@@ -25,6 +25,7 @@
 #include "alloc/DdmHeap.h"
 #include "alloc/HeapSource.h"
 #include "alloc/MarkSweep.h"
+#include "alloc/Visit.h"
 
 #include "utils/threads.h"      // need Android thread priorities
 #define kInvalidPriority        10000
@@ -582,11 +583,10 @@
 /*
  * Scan every live object in the heap, holding the locks.
  */
-static void verifyHeap()
+static void verifyHeap(void)
 {
-    // TODO: check the locks.
-    HeapBitmap *liveBits = dvmHeapSourceGetLiveBits();
-    dvmVerifyBitmap(liveBits);
+    dvmVerifyRoots();
+    dvmVerifyBitmap(dvmHeapSourceGetLiveBits());
 }
 
 /*
@@ -757,6 +757,7 @@
          * heap to allow mutator threads to allocate from free space.
          */
         rootEnd = dvmGetRelativeTimeMsec();
+        dvmClearCardTable();
         dvmUnlockHeap();
         dvmResumeAllThreads(SUSPEND_FOR_GC);
         rootSuspendTime = rootStart - suspendStart;
@@ -783,12 +784,12 @@
          * As no barrier intercepts root updates, we conservatively
          * assume all roots may be gray and re-mark them.
          */
-        dvmHeapMarkRootSet();
+        dvmHeapReMarkRootSet();
         /*
          * Recursively mark gray objects pointed to by the roots or by
          * heap objects dirtied during the concurrent mark.
          */
-        dvmMarkDirtyObjects();
+        dvmHeapReScanMarkedObjects();
     }
 
     /* All strongly-reachable objects have now been marked.
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index 574a731..94179e4 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -112,8 +112,7 @@
 }
 
 static void
-markObjectNonNull(const Object *obj, GcMarkContext *ctx,
-        bool checkFinger, bool forceStack)
+markObjectNonNull(const Object *obj, GcMarkContext *ctx, bool checkFinger)
 {
     assert(ctx != NULL);
     assert(obj != NULL);
@@ -126,7 +125,7 @@
     if (!setAndReturnMarkBit(ctx, obj)) {
         /* This object was not previously marked.
          */
-        if (forceStack || (checkFinger && (void *)obj < ctx->finger)) {
+        if (checkFinger && (void *)obj < ctx->finger) {
             /* This object will need to go on the mark stack.
              */
             MARK_STACK_PUSH(ctx->stack, obj);
@@ -149,7 +148,7 @@
 static void markObject(const Object *obj, GcMarkContext *ctx)
 {
     if (obj != NULL) {
-        markObjectNonNull(obj, ctx, true, false);
+        markObjectNonNull(obj, ctx, true);
     }
 }
 
@@ -166,7 +165,7 @@
 dvmMarkObjectNonNull(const Object *obj)
 {
     assert(obj != NULL);
-    markObjectNonNull(obj, &gDvm.gcHeap->markContext, false, false);
+    markObjectNonNull(obj, &gDvm.gcHeap->markContext, false);
 }
 
 /* Mark the set of root objects.
@@ -256,6 +255,32 @@
 }
 
 /*
+ * Callback applied to root references.  If the root location contains
+ * a white reference it is pushed on the mark stack and grayed.
+ */
+static void markObjectVisitor(void *addr, void *arg)
+{
+    Object *obj;
+
+    assert(addr != NULL);
+    assert(arg != NULL);
+    obj = *(Object **)addr;
+    if (obj != NULL) {
+        markObjectNonNull(obj, arg, true);
+    }
+}
+
+/*
+ * Grays all references in the roots.
+ */
+void dvmHeapReMarkRootSet(void)
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    assert(ctx->finger == (void *)ULONG_MAX);
+    dvmVisitRoots(markObjectVisitor, ctx);
+}
+
+/*
  * Nothing past this point is allowed to use dvmMarkObject() or
  * dvmMarkObjectNonNull(), which are for root-marking only.
  * Scanning/recursion must use markObject(), which takes the finger
@@ -524,6 +549,107 @@
     }
 }
 
+static size_t objectSize(const Object *obj)
+{
+    assert(dvmIsValidObject(obj));
+    assert(dvmIsValidObject((Object *)obj->clazz));
+    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+        return dvmArrayObjectSize((ArrayObject *)obj);
+    } else if (obj->clazz == gDvm.classJavaLangClass) {
+        return dvmClassObjectSize((ClassObject *)obj);
+    } else {
+        return obj->clazz->objectSize;
+    }
+}
+
+/*
+ * Scans backward to the header of a marked object that spans the
+ * given address.  Returns NULL if there is no spanning marked object.
+ */
+static Object *previousGrayObject(u1 *start, GcMarkContext *ctx)
+{
+    u1 *end = (u1 *)ctx->bitmap->base;
+    u1 *ptr;
+
+    assert(start >= end);
+    for (ptr = start; ptr >= end; ptr -= HB_OBJECT_ALIGNMENT) {
+        if (dvmIsValidObject((Object *)ptr))
+            break;
+    }
+    if (ptr < end || !isMarked(ptr, ctx)) {
+         return NULL;
+    } else {
+        Object *obj = (Object *)ptr;
+        size_t size = objectSize(obj);
+        if (ptr + size < start) {
+            return NULL;
+        }
+        return obj;
+    }
+}
+
+/*
+ * Scans forward to the header of the next marked object between start
+ * and limit.  Returns NULL if no marked objects are in that region.
+ */
+static Object *nextGrayObject(u1 *base, u1 *limit, GcMarkContext *ctx)
+{
+    u1 *ptr;
+
+    assert(base < limit);
+    assert(limit - base <= GC_CARD_SIZE);
+    for (ptr = base; ptr < limit; ptr += HB_OBJECT_ALIGNMENT) {
+        if (isMarked(ptr, ctx))
+            return (Object *)ptr;
+    }
+    return NULL;
+}
+
+/*
+ * Scan the card table looking for objects that have been grayed by
+ * the mutator.
+ */
+static void scanGrayObjects(GcMarkContext *ctx)
+{
+    GcHeap *h = gDvm.gcHeap;
+    u1 *card, *baseCard, *limitCard;
+
+    baseCard = &h->cardTableBase[0];
+    limitCard = &h->cardTableBase[h->cardTableLength];
+    for (card = baseCard; card != limitCard; ++card) {
+        if (*card == GC_CARD_DIRTY) {
+            /*
+             * The card is dirty.  Scan all of the objects that
+             * intersect with the card address.
+             */
+            u1 *addr = dvmAddrFromCard(card);
+            /*
+             * If the last object on the previous card terminates on
+             * the current card it is gray and must be scanned.
+             */
+            if (!dvmIsValidObject((Object *)addr)) {
+                Object *prev = previousGrayObject(addr, ctx);
+                if (prev != NULL) {
+                    scanObject(prev, ctx);
+                }
+            }
+            /*
+             * Scan through all black objects that start on the
+             * current card.
+             */
+            u1 *limit = addr + GC_CARD_SIZE;
+            u1 *next = addr;
+            while (next < limit) {
+                Object *obj = nextGrayObject(next, limit, ctx);
+                if (obj == NULL)
+                    break;
+                scanObject(obj, ctx);
+                next = (u1*)obj + HB_OBJECT_ALIGNMENT;
+            }
+        }
+    }
+}
+
 #ifndef NDEBUG
 static uintptr_t gLastFinger = 0;
 #endif
@@ -573,43 +699,16 @@
     LOG_SCAN("done with marked objects\n");
 }
 
-/*
- * Callback applied to each gray object to blacken it.
- */
-static bool dirtyObjectCallback(size_t numPtrs, void **ptrs,
-                                const void *finger, void *arg)
+void dvmHeapReScanMarkedObjects(void)
 {
-    size_t i;
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
 
-    for (i = 0; i < numPtrs; ++i) {
-        scanObject(ptrs[i], arg);
-    }
-    return true;
-}
-
-/*
- * Re-mark dirtied objects.  Iterates through all blackened objects
- * looking for references to white objects.
- */
-void dvmMarkDirtyObjects(void)
-{
-    HeapBitmap markBits[HEAP_SOURCE_MAX_HEAP_COUNT];
-    HeapBitmap liveBits[HEAP_SOURCE_MAX_HEAP_COUNT];
-    GcMarkContext *ctx;
-    size_t numBitmaps;
-    size_t i;
-
-    ctx = &gDvm.gcHeap->markContext;
     /*
      * The finger must have been set to the maximum value to ensure
      * that gray objects will be pushed onto the mark stack.
      */
     assert(ctx->finger == (void *)ULONG_MAX);
-    numBitmaps = dvmHeapSourceGetNumHeaps();
-    dvmHeapSourceGetObjectBitmaps(liveBits, markBits, numBitmaps);
-    for (i = 0; i < numBitmaps; i++) {
-        dvmHeapBitmapWalk(&markBits[i], dirtyObjectCallback, ctx);
-    }
+    scanGrayObjects(ctx);
     processMarkStack(ctx);
 }
 
diff --git a/vm/alloc/MarkSweep.h b/vm/alloc/MarkSweep.h
index e143b35..46066c6 100644
--- a/vm/alloc/MarkSweep.h
+++ b/vm/alloc/MarkSweep.h
@@ -47,8 +47,9 @@
 
 bool dvmHeapBeginMarkStep(GcMode mode);
 void dvmHeapMarkRootSet(void);
+void dvmHeapReMarkRootSet(void);
 void dvmHeapScanMarkedObjects(void);
-void dvmMarkDirtyObjects(void);
+void dvmHeapReScanMarkedObjects(void);
 void dvmHandleSoftRefs(Object **list);
 void dvmClearWhiteRefs(Object **list);
 void dvmHeapScheduleFinalizations(void);
diff --git a/vm/alloc/Visit.c b/vm/alloc/Visit.c
index 0124e29..9a799f2 100644
--- a/vm/alloc/Visit.c
+++ b/vm/alloc/Visit.c
@@ -201,5 +201,8 @@
     visitLargeHeapRefTable(visitor, gDvm.gcHeap->referenceOperations, arg);
     visitLargeHeapRefTable(visitor, gDvm.gcHeap->pendingFinalizationRefs, arg);
     visitThreads(visitor, arg);
+    (*visitor)(&gDvm.outOfMemoryObj, arg);
+    (*visitor)(&gDvm.internalErrorObj, arg);
+    (*visitor)(&gDvm.noClassDefFoundErrorObj, arg);
     /* TODO: visit cached global references. */
 }