A simple promotion-like mechanism.

If enabled, this mechanism moves objects that survive the first
semi-space copying collection to the non-moving space.

Bug: 11650816
Change-Id: Ia2fc902d08dc983449416f420a39449f9ed96255
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 3939354..31a3f35 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -78,6 +78,8 @@
 
 static constexpr bool kProtectFromSpace = true;
 static constexpr bool kResetFromSpace = true;
+// TODO: move this to a new file as a new garbage collector?
+static constexpr bool kEnableSimplePromo = false;
 
 // TODO: Unduplicate logic.
 void SemiSpace::ImmuneSpace(space::ContinuousSpace* space) {
@@ -134,7 +136,9 @@
       finalizer_reference_list_(nullptr),
       phantom_reference_list_(nullptr),
       cleared_reference_list_(nullptr),
-      self_(nullptr) {
+      self_(nullptr),
+      last_gc_to_space_end_(nullptr),
+      bytes_promoted_(0) {
 }
 
 void SemiSpace::InitializePhase() {
@@ -169,6 +173,17 @@
   // Need to do this with mutators paused so that somebody doesn't accidentally allocate into the
   // wrong space.
   heap_->SwapSemiSpaces();
+  if (kEnableSimplePromo) {
+    // If last_gc_to_space_end_ is out of the bounds of the from-space
+    // (the to-space from last GC), then point it to the beginning of
+    // the from-space. For example, the very first GC or the
+    // pre-zygote compaction.
+    if (!from_space_->HasAddress(reinterpret_cast<mirror::Object*>(last_gc_to_space_end_))) {
+      last_gc_to_space_end_ = from_space_->Begin();
+    }
+    // Reset this before the marking starts below.
+    bytes_promoted_ = 0;
+  }
   // Assume the cleared space is already empty.
   BindBitmaps();
   // Process dirty cards and add dirty cards to mod-union tables.
@@ -268,6 +283,13 @@
   } else {
     mprotect(from_space_->Begin(), from_space_->Capacity(), PROT_READ);
   }
+
+  if (kEnableSimplePromo) {
+    // Record the end (top) of the to space so we can distinguish
+    // between objects that were allocated since the last GC and the
+    // older objects.
+    last_gc_to_space_end_ = to_space_->End();
+  }
 }
 
 void SemiSpace::ResizeMarkStack(size_t new_size) {
@@ -308,11 +330,38 @@
     if (from_space_->HasAddress(obj)) {
       mirror::Object* forward_address = GetForwardingAddressInFromSpace(obj);
       // If the object has already been moved, return the new forward address.
-      if (!to_space_->HasAddress(forward_address)) {
+      if (forward_address == nullptr) {
         // Otherwise, we need to move the object and add it to the markstack for processing.
         size_t object_size = obj->SizeOf();
         size_t dummy = 0;
-        forward_address = to_space_->Alloc(self_, object_size, &dummy);
+        if (kEnableSimplePromo && reinterpret_cast<byte*>(obj) < last_gc_to_space_end_) {
+          // If it's allocated before the last GC (older), move (pseudo-promote) it to
+          // the non-moving space (as sort of an old generation.)
+          size_t bytes_promoted;
+          space::MallocSpace* non_moving_space = GetHeap()->GetNonMovingSpace();
+          forward_address = non_moving_space->Alloc(self_, object_size, &bytes_promoted);
+          if (forward_address == nullptr) {
+            // If out of space, fall back to the to-space.
+            forward_address = to_space_->Alloc(self_, object_size, &dummy);
+          } else {
+            GetHeap()->num_bytes_allocated_.fetch_add(bytes_promoted);
+            bytes_promoted_ += bytes_promoted;
+            // Mark forward_address on the live bit map.
+            accounting::SpaceBitmap* live_bitmap = non_moving_space->GetLiveBitmap();
+            DCHECK(live_bitmap != nullptr);
+            DCHECK(!live_bitmap->Test(forward_address));
+            live_bitmap->Set(forward_address);
+            // Mark forward_address on the mark bit map.
+            accounting::SpaceBitmap* mark_bitmap = non_moving_space->GetMarkBitmap();
+            DCHECK(mark_bitmap != nullptr);
+            DCHECK(!mark_bitmap->Test(forward_address));
+            mark_bitmap->Set(forward_address);
+          }
+          DCHECK(forward_address != nullptr);
+        } else {
+          // If it's allocated after the last GC (younger), copy it to the to-space.
+          forward_address = to_space_->Alloc(self_, object_size, &dummy);
+        }
         // Copy over the object and add it to the mark stack since we still need to update it's
         // references.
         memcpy(reinterpret_cast<void*>(forward_address), obj, object_size);
@@ -322,6 +371,9 @@
                        monitor_size_must_be_same_as_object);
         obj->SetLockWord(LockWord::FromForwardingAddress(reinterpret_cast<size_t>(forward_address)));
         MarkStackPush(forward_address);
+      } else {
+        DCHECK(to_space_->HasAddress(forward_address) ||
+               (kEnableSimplePromo && GetHeap()->GetNonMovingSpace()->HasAddress(forward_address)));
       }
       ret = forward_address;
       // TODO: Do we need this if in the else statement?
@@ -535,7 +587,9 @@
   if (from_space_->HasAddress(obj)) {
     mirror::Object* forwarding_address = GetForwardingAddressInFromSpace(const_cast<Object*>(obj));
     // If the object is forwarded then it MUST be marked.
-    if (to_space_->HasAddress(forwarding_address)) {
+    DCHECK(forwarding_address == nullptr || to_space_->HasAddress(forwarding_address) ||
+           (kEnableSimplePromo && GetHeap()->GetNonMovingSpace()->HasAddress(forwarding_address)));
+    if (forwarding_address != nullptr) {
       return forwarding_address;
     }
     // Must not be marked, return nullptr;
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 0f0cae1..b0724f9 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -281,6 +281,15 @@
 
   Thread* self_;
 
+  // Used for kEnableSimplePromo. The end/top of the bump pointer
+  // space at the end of the last collection.
+  byte* last_gc_to_space_end_;
+
+  // Used for kEnableSimplePromo. During a collection, keeps track of
+  // how many bytes of objects have been copied so far from the bump
+  // pointer space to the non-moving space.
+  uint64_t bytes_promoted_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(SemiSpace);
 };