Store class tables in the image

Reduces how long it takes to load an application image.

N5 boot.art size
Before: 8007680
After: 8122368

Also reduces boot time by how long AddImageClassesToClassTable
used to take (~20ms).

Changed class hashes to be uint32_t to fix cross compilation. We need
serialized hash tables to be valid with different pointer sizes.

Bug: 22858531

Change-Id: I463fc83f499ff75f509e80c253a55b9116ee5b89
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 341742e..bf1fcdd 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -540,7 +540,10 @@
 }
 
 bool ImageWriter::AllocMemory() {
-  const size_t length = RoundUp(image_objects_offset_begin_ + GetBinSizeSum() + intern_table_bytes_,
+  const size_t length = RoundUp(image_objects_offset_begin_ +
+                                    GetBinSizeSum() +
+                                    intern_table_bytes_ +
+                                    class_table_bytes_,
                                 kPageSize);
   std::string error_msg;
   image_.reset(MemMap::MapAnonymous("image writer image",
@@ -1030,6 +1033,14 @@
           WalkFieldsInOrder(value);
         }
       }
+    } else if (h_obj->IsClassLoader()) {
+      // Register the class loader if it has a class table.
+      // The fake boot class loader should not get registered and we should end up with only one
+      // class loader.
+      mirror::ClassLoader* class_loader = h_obj->AsClassLoader();
+      if (class_loader->GetClassTable() != nullptr) {
+        class_loaders_.insert(class_loader);
+      }
     }
   }
 }
@@ -1154,10 +1165,26 @@
   }
 
   // Calculate how big the intern table will be after being serialized.
-  auto* const intern_table = Runtime::Current()->GetInternTable();
+  InternTable* const intern_table = runtime->GetInternTable();
   CHECK_EQ(intern_table->WeakSize(), 0u) << " should have strong interned all the strings";
   intern_table_bytes_ = intern_table->WriteToMemory(nullptr);
 
+  // Write out the class table.
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  if (boot_image_space_ == nullptr) {
+    // Compiling the boot image, add null class loader.
+    class_loaders_.insert(nullptr);
+  }
+  if (!class_loaders_.empty()) {
+    CHECK_EQ(class_loaders_.size(), 1u) << "Should only have one real class loader in the image";
+    ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+    for (mirror::ClassLoader* loader : class_loaders_) {
+      ClassTable* table = class_linker->ClassTableForClassLoader(loader);
+      CHECK(table != nullptr);
+      class_table_bytes_ += table->WriteToMemory(nullptr);
+    }
+  }
+
   // Note that image_end_ is left at end of used mirror object section.
 }
 
@@ -1199,6 +1226,12 @@
   auto* interned_strings_section = &sections[ImageHeader::kSectionInternedStrings];
   *interned_strings_section = ImageSection(cur_pos, intern_table_bytes_);
   cur_pos = interned_strings_section->End();
+  // Calculate the size of the class table section.
+  auto* class_table_section = &sections[ImageHeader::kSectionClassTable];
+  *class_table_section = ImageSection(cur_pos, class_table_bytes_);
+  cur_pos = class_table_section->End();
+  // Image end goes right before the start of the image bitmap.
+  const size_t image_end = static_cast<uint32_t>(cur_pos);
   // Finally bitmap section.
   const size_t bitmap_bytes = image_bitmap_->Size();
   auto* bitmap_section = &sections[ImageHeader::kSectionImageBitmap];
@@ -1212,7 +1245,6 @@
     }
     LOG(INFO) << "Methods: clean=" << clean_methods_ << " dirty=" << dirty_methods_;
   }
-  const size_t image_end = static_cast<uint32_t>(interned_strings_section->End());
   CHECK_EQ(AlignUp(image_begin_ + image_end, kPageSize), oat_file_begin) <<
       "Oat file should be right after the image.";
   // Create the header.
@@ -1323,23 +1355,48 @@
     }
     image_header->SetImageMethod(static_cast<ImageHeader::ImageMethod>(i), method);
   }
+  FixupRootVisitor root_visitor(this);
+
   // Write the intern table into the image.
   const ImageSection& intern_table_section = image_header->GetImageSection(
       ImageHeader::kSectionInternedStrings);
-  InternTable* const intern_table = Runtime::Current()->GetInternTable();
-  uint8_t* const memory_ptr = image_->Begin() + intern_table_section.Offset();
-  const size_t intern_table_bytes = intern_table->WriteToMemory(memory_ptr);
+  Runtime* const runtime = Runtime::Current();
+  InternTable* const intern_table = runtime->GetInternTable();
+  uint8_t* const intern_table_memory_ptr = image_->Begin() + intern_table_section.Offset();
+  const size_t intern_table_bytes = intern_table->WriteToMemory(intern_table_memory_ptr);
+  CHECK_EQ(intern_table_bytes, intern_table_bytes_);
   // Fixup the pointers in the newly written intern table to contain image addresses.
-  InternTable temp_table;
+  InternTable temp_intern_table;
   // Note that we require that ReadFromMemory does not make an internal copy of the elements so that
   // the VisitRoots() will update the memory directly rather than the copies.
   // This also relies on visit roots not doing any verification which could fail after we update
   // the roots to be the image addresses.
-  temp_table.ReadFromMemory(memory_ptr);
-  CHECK_EQ(temp_table.Size(), intern_table->Size());
-  FixupRootVisitor visitor(this);
-  temp_table.VisitRoots(&visitor, kVisitRootFlagAllRoots);
-  CHECK_EQ(intern_table_bytes, intern_table_bytes_);
+  temp_intern_table.ReadFromMemory(intern_table_memory_ptr);
+  CHECK_EQ(temp_intern_table.Size(), intern_table->Size());
+  temp_intern_table.VisitRoots(&root_visitor, kVisitRootFlagAllRoots);
+
+  // Write the class table(s) into the image.
+  ClassLinker* const class_linker = runtime->GetClassLinker();
+  const ImageSection& class_table_section = image_header->GetImageSection(
+      ImageHeader::kSectionClassTable);
+  uint8_t* const class_table_memory_ptr = image_->Begin() + class_table_section.Offset();
+  ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  size_t class_table_bytes = 0;
+  for (mirror::ClassLoader* loader : class_loaders_) {
+    ClassTable* table = class_linker->ClassTableForClassLoader(loader);
+    CHECK(table != nullptr);
+    uint8_t* memory_ptr = class_table_memory_ptr + class_table_bytes;
+    class_table_bytes += table->WriteToMemory(memory_ptr);
+    // Fixup the pointers in the newly written class table to contain image addresses. See
+    // above comment for intern tables.
+    ClassTable temp_class_table;
+    temp_class_table.ReadFromMemory(memory_ptr);
+    // CHECK_EQ(temp_class_table.NumNonZygoteClasses(), table->NumNonZygoteClasses());
+    BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(&root_visitor,
+                                                                    RootInfo(kRootUnknown));
+    temp_class_table.VisitRoots(buffered_visitor);
+  }
+  CHECK_EQ(class_table_bytes, class_table_bytes_);
 }
 
 void ImageWriter::CopyAndFixupObjects() {
@@ -1553,8 +1610,7 @@
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
       if (klass == class_linker->GetClassRoot(ClassLinker::kJavaLangDexCache)) {
         FixupDexCache(down_cast<mirror::DexCache*>(orig), down_cast<mirror::DexCache*>(copy));
-      } else if (klass->IsSubClass(down_cast<mirror::Class*>(
-          class_linker->GetClassRoot(ClassLinker::kJavaLangClassLoader)))) {
+      } else if (klass->IsClassLoaderClass()) {
         // If src is a ClassLoader, set the class table to null so that it gets recreated by the
         // ClassLoader.
         down_cast<mirror::ClassLoader*>(copy)->SetClassTable(nullptr);
@@ -1840,7 +1896,8 @@
                                 bin_slot_sizes_[kBinArtMethodDirty] +
                                 bin_slot_sizes_[kBinArtMethodClean] +
                                 bin_slot_sizes_[kBinDexCacheArray] +
-                                intern_table_bytes_;
+                                intern_table_bytes_ +
+                                class_table_bytes_;
   return image_begin_ + RoundUp(image_end_ + native_sections_size, kPageSize);
 }