Use cleared JNI weak sentinel from boot image.

We were already adding the sentinel to the boot image,
so we may as well reuse the boot image copy.

Also move pre-allocated objects from class roots to the
boot image live objects.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: aosp_taimen-userdebug boots.
Change-Id: I635dcdd146ca2c6b55d187e9a545a9990b0b35ca
diff --git a/compiler/optimizing/intrinsic_objects.cc b/compiler/optimizing/intrinsic_objects.cc
index c345624..5f6f562 100644
--- a/compiler/optimizing/intrinsic_objects.cc
+++ b/compiler/optimizing/intrinsic_objects.cc
@@ -17,18 +17,18 @@
 #include "intrinsic_objects.h"
 
 #include "art_field-inl.h"
+#include "base/casts.h"
 #include "base/logging.h"
-#include "class_root.h"
-#include "handle.h"
+#include "image.h"
 #include "obj_ptr-inl.h"
-#include "mirror/object_array-alloc-inl.h"
-#include "mirror/object_array-inl.h"
 
 namespace art {
 
-static ObjPtr<mirror::ObjectArray<mirror::Object>> LookupIntegerCache(Thread* self,
-                                                                      ClassLinker* class_linker)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
+static constexpr size_t kIntrinsicObjectsOffset =
+    enum_cast<size_t>(ImageHeader::kIntrinsicObjectsStart);
+
+ObjPtr<mirror::ObjectArray<mirror::Object>> IntrinsicObjects::LookupIntegerCache(
+    Thread* self, ClassLinker* class_linker) {
   ObjPtr<mirror::Class> integer_cache_class = class_linker->LookupClass(
       self, "Ljava/lang/Integer$IntegerCache;", /* class_loader= */ nullptr);
   if (integer_cache_class == nullptr || !integer_cache_class->IsInitialized()) {
@@ -44,47 +44,24 @@
   return integer_cache;
 }
 
-ObjPtr<mirror::ObjectArray<mirror::Object>> IntrinsicObjects::AllocateBootImageLiveObjects(
-    Thread* self,
-    ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_) {
-  // The objects used for the Integer.valueOf() intrinsic must remain live even if references
-  // to them are removed using reflection. Image roots are not accessible through reflection,
-  // so the array we construct here shall keep them alive.
-  StackHandleScope<1> hs(self);
-  Handle<mirror::ObjectArray<mirror::Object>> integer_cache =
-      hs.NewHandle(LookupIntegerCache(self, class_linker));
-  size_t live_objects_size =
-      (integer_cache != nullptr) ? (/* cache */ 1u + integer_cache->GetLength()) : 0u;
-  ObjPtr<mirror::ObjectArray<mirror::Object>> live_objects =
-      mirror::ObjectArray<mirror::Object>::Alloc(
-          self, GetClassRoot<mirror::ObjectArray<mirror::Object>>(class_linker), live_objects_size);
-  int32_t index = 0;
-  if (integer_cache != nullptr) {
-    live_objects->Set(index++, integer_cache.Get());
-    for (int32_t i = 0, length = integer_cache->GetLength(); i != length; ++i) {
-      live_objects->Set(index++, integer_cache->Get(i));
-    }
-  }
-  CHECK_EQ(index, live_objects->GetLength());
-
-  if (kIsDebugBuild && integer_cache != nullptr) {
-    CHECK_EQ(integer_cache.Get(), GetIntegerValueOfCache(live_objects));
-    for (int32_t i = 0, len = integer_cache->GetLength(); i != len; ++i) {
-      CHECK_EQ(integer_cache->GetWithoutChecks(i), GetIntegerValueOfObject(live_objects, i));
-    }
-  }
-  return live_objects;
+static bool HasIntrinsicObjects(
+    ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK(boot_image_live_objects != nullptr);
+  uint32_t length = static_cast<uint32_t>(boot_image_live_objects->GetLength());
+  DCHECK_GE(length, kIntrinsicObjectsOffset);
+  return length != kIntrinsicObjectsOffset;
 }
 
 ObjPtr<mirror::ObjectArray<mirror::Object>> IntrinsicObjects::GetIntegerValueOfCache(
     ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects) {
-  DCHECK(boot_image_live_objects != nullptr);
-  if (boot_image_live_objects->GetLength() == 0u) {
+  if (!HasIntrinsicObjects(boot_image_live_objects)) {
     return nullptr;  // No intrinsic objects.
   }
   // No need for read barrier for boot image object or for verifying the value that was just stored.
   ObjPtr<mirror::Object> result =
-      boot_image_live_objects->GetWithoutChecks<kVerifyNone, kWithoutReadBarrier>(0);
+      boot_image_live_objects->GetWithoutChecks<kVerifyNone, kWithoutReadBarrier>(
+          kIntrinsicObjectsOffset);
   DCHECK(result != nullptr);
   DCHECK(result->IsObjectArray());
   DCHECK(result->GetClass()->DescriptorEquals("[Ljava/lang/Integer;"));
@@ -94,15 +71,14 @@
 ObjPtr<mirror::Object> IntrinsicObjects::GetIntegerValueOfObject(
     ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects,
     uint32_t index) {
-  DCHECK(boot_image_live_objects != nullptr);
-  DCHECK_NE(boot_image_live_objects->GetLength(), 0);
+  DCHECK(HasIntrinsicObjects(boot_image_live_objects));
   DCHECK_LT(index,
             static_cast<uint32_t>(GetIntegerValueOfCache(boot_image_live_objects)->GetLength()));
 
   // No need for read barrier for boot image object or for verifying the value that was just stored.
   ObjPtr<mirror::Object> result =
       boot_image_live_objects->GetWithoutChecks<kVerifyNone, kWithoutReadBarrier>(
-          /* skip the IntegerCache.cache */ 1u + index);
+          kIntrinsicObjectsOffset + /* skip the IntegerCache.cache */ 1u + index);
   DCHECK(result != nullptr);
   DCHECK(result->GetClass()->DescriptorEquals("Ljava/lang/Integer;"));
   return result;
@@ -110,8 +86,9 @@
 
 MemberOffset IntrinsicObjects::GetIntegerValueOfArrayDataOffset(
     ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects) {
-  DCHECK_NE(boot_image_live_objects->GetLength(), 0);
-  MemberOffset result = mirror::ObjectArray<mirror::Object>::OffsetOfElement(1u);
+  DCHECK(HasIntrinsicObjects(boot_image_live_objects));
+  MemberOffset result =
+      mirror::ObjectArray<mirror::Object>::OffsetOfElement(kIntrinsicObjectsOffset + 1u);
   DCHECK_EQ(GetIntegerValueOfObject(boot_image_live_objects, 0u),
             (boot_image_live_objects
                  ->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(result)));
diff --git a/compiler/optimizing/intrinsic_objects.h b/compiler/optimizing/intrinsic_objects.h
index 863017b..ed764bd 100644
--- a/compiler/optimizing/intrinsic_objects.h
+++ b/compiler/optimizing/intrinsic_objects.h
@@ -55,11 +55,9 @@
     return IndexField::Decode(intrinsic_data);
   }
 
-  static ObjPtr<mirror::ObjectArray<mirror::Object>> AllocateBootImageLiveObjects(
-      Thread* self,
-      ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Functions for retrieving data for Integer.valueOf().
+  static ObjPtr<mirror::ObjectArray<mirror::Object>> LookupIntegerCache(
+      Thread* self, ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_);
   static ObjPtr<mirror::ObjectArray<mirror::Object>> GetIntegerValueOfCache(
       ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects)
       REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 262b224..afd221f 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -148,6 +148,56 @@
 // Separate objects into multiple bins to optimize dirty memory use.
 static constexpr bool kBinObjects = true;
 
+ObjPtr<mirror::ObjectArray<mirror::Object>> AllocateBootImageLiveObjects(
+    Thread* self, Runtime* runtime) REQUIRES_SHARED(Locks::mutator_lock_) {
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  // The objects used for the Integer.valueOf() intrinsic must remain live even if references
+  // to them are removed using reflection. Image roots are not accessible through reflection,
+  // so the array we construct here shall keep them alive.
+  StackHandleScope<1> hs(self);
+  Handle<mirror::ObjectArray<mirror::Object>> integer_cache =
+      hs.NewHandle(IntrinsicObjects::LookupIntegerCache(self, class_linker));
+  size_t live_objects_size =
+      enum_cast<size_t>(ImageHeader::kIntrinsicObjectsStart) +
+      ((integer_cache != nullptr) ? (/* cache */ 1u + integer_cache->GetLength()) : 0u);
+  ObjPtr<mirror::ObjectArray<mirror::Object>> live_objects =
+      mirror::ObjectArray<mirror::Object>::Alloc(
+          self, GetClassRoot<mirror::ObjectArray<mirror::Object>>(class_linker), live_objects_size);
+  int32_t index = 0u;
+  auto set_entry = [&](ImageHeader::BootImageLiveObjects entry,
+                       ObjPtr<mirror::Object> value) REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK_EQ(index, enum_cast<int32_t>(entry));
+    live_objects->Set</*kTransacrionActive=*/ false>(index, value);
+    ++index;
+  };
+  set_entry(ImageHeader::kOomeWhenThrowingException,
+            runtime->GetPreAllocatedOutOfMemoryErrorWhenThrowingException());
+  set_entry(ImageHeader::kOomeWhenThrowingOome,
+            runtime->GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME());
+  set_entry(ImageHeader::kOomeWhenHandlingStackOverflow,
+            runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
+  set_entry(ImageHeader::kNoClassDefFoundError, runtime->GetPreAllocatedNoClassDefFoundError());
+  set_entry(ImageHeader::kClearedJniWeakSentinel, runtime->GetSentinel().Read());
+
+  DCHECK_EQ(index, enum_cast<int32_t>(ImageHeader::kIntrinsicObjectsStart));
+  if (integer_cache != nullptr) {
+    live_objects->Set(index++, integer_cache.Get());
+    for (int32_t i = 0, length = integer_cache->GetLength(); i != length; ++i) {
+      live_objects->Set(index++, integer_cache->Get(i));
+    }
+  }
+  CHECK_EQ(index, live_objects->GetLength());
+
+  if (kIsDebugBuild && integer_cache != nullptr) {
+    CHECK_EQ(integer_cache.Get(), IntrinsicObjects::GetIntegerValueOfCache(live_objects));
+    for (int32_t i = 0, len = integer_cache->GetLength(); i != len; ++i) {
+      CHECK_EQ(integer_cache->GetWithoutChecks(i),
+               IntrinsicObjects::GetIntegerValueOfObject(live_objects, i));
+    }
+  }
+  return live_objects;
+}
+
 ObjPtr<mirror::ClassLoader> ImageWriter::GetAppClassLoader() const
     REQUIRES_SHARED(Locks::mutator_lock_) {
   return compiler_options_.IsAppImage()
@@ -1921,14 +1971,6 @@
       self, GetClassRoot<ObjectArray<Object>>(class_linker), image_roots_size)));
   image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
   image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots());
-  image_roots->Set<false>(ImageHeader::kOomeWhenThrowingException,
-                          runtime->GetPreAllocatedOutOfMemoryErrorWhenThrowingException());
-  image_roots->Set<false>(ImageHeader::kOomeWhenThrowingOome,
-                          runtime->GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME());
-  image_roots->Set<false>(ImageHeader::kOomeWhenHandlingStackOverflow,
-                          runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
-  image_roots->Set<false>(ImageHeader::kNoClassDefFoundError,
-                          runtime->GetPreAllocatedNoClassDefFoundError());
   if (!compiler_options_.IsAppImage()) {
     DCHECK(boot_image_live_objects != nullptr);
     image_roots->Set<false>(ImageHeader::kBootImageLiveObjects, boot_image_live_objects.Get());
@@ -2260,7 +2302,7 @@
   MutableHandle<ObjectArray<Object>> boot_image_live_objects = handles.NewHandle(
       compiler_options_.IsAppImage()
           ? nullptr
-          : IntrinsicObjects::AllocateBootImageLiveObjects(self, runtime->GetClassLinker()));
+          : AllocateBootImageLiveObjects(self, runtime));
   std::vector<Handle<ObjectArray<Object>>> image_roots;
   for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) {
     image_roots.push_back(handles.NewHandle(CreateImageRoots(i, boot_image_live_objects)));
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 5b7535d..090e271 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -110,10 +110,6 @@
 const char* image_roots_descriptions_[] = {
   "kDexCaches",
   "kClassRoots",
-  "kOomeWhenThrowingException",
-  "kOomeWhenThrowingOome",
-  "kOomeWhenHandlingStackOverflow",
-  "kNoClassDefFoundError",
   "kSpecialRoots",
 };
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 1343b16..b2e7d99 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -951,12 +951,12 @@
   gc::Heap* const heap = runtime->GetHeap();
   std::vector<gc::space::ImageSpace*> spaces = heap->GetBootImageSpaces();
   CHECK(!spaces.empty());
-  uint32_t pointer_size_unchecked = spaces[0]->GetImageHeader().GetPointerSizeUnchecked();
+  const ImageHeader& image_header = spaces[0]->GetImageHeader();
+  uint32_t pointer_size_unchecked = image_header.GetPointerSizeUnchecked();
   if (!ValidPointerSize(pointer_size_unchecked)) {
     *error_msg = StringPrintf("Invalid image pointer size: %u", pointer_size_unchecked);
     return false;
   }
-  const ImageHeader& image_header = spaces[0]->GetImageHeader();
   image_pointer_size_ = image_header.GetPointerSize();
   if (!runtime->IsAotCompiler()) {
     // Only the Aot compiler supports having an image with a different pointer size than the
@@ -1057,15 +1057,15 @@
 
   class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(
       ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(
-          spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)));
+          image_header.GetImageRoot(ImageHeader::kClassRoots)));
   DCHECK_EQ(GetClassRoot<mirror::Class>(this)->GetClassFlags(), mirror::kClassFlagClass);
 
-  ObjPtr<mirror::Class> java_lang_Object = GetClassRoot<mirror::Object>(this);
-  java_lang_Object->SetObjectSize(sizeof(mirror::Object));
-  // Allocate in non-movable so that it's possible to check if a JNI weak global ref has been
-  // cleared without triggering the read barrier and unintentionally mark the sentinel alive.
-  runtime->SetSentinel(heap->AllocNonMovableObject(
-      self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor()));
+  DCHECK_EQ(GetClassRoot<mirror::Object>(this)->GetObjectSize(), sizeof(mirror::Object));
+  ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects =
+      ObjPtr<mirror::ObjectArray<mirror::Object>>::DownCast(
+          image_header.GetImageRoot(ImageHeader::kBootImageLiveObjects));
+  runtime->SetSentinel(boot_image_live_objects->Get(ImageHeader::kClearedJniWeakSentinel));
+  DCHECK(runtime->GetSentinel().Read()->GetClass() == GetClassRoot<mirror::Object>(this));
 
   const std::vector<std::string>& boot_class_path_locations = runtime->GetBootClassPathLocations();
   CHECK_LE(spaces.size(), boot_class_path_locations.size());
diff --git a/runtime/image.cc b/runtime/image.cc
index 64046d0..0209a29 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -29,7 +29,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '7', '6', '\0' };  // SBAppend simplification.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '7', '7', '\0' };  // Use boot image sentinel.
 
 ImageHeader::ImageHeader(uint32_t image_reservation_size,
                          uint32_t component_count,
diff --git a/runtime/image.h b/runtime/image.h
index 88bba13..4626055 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -222,10 +222,6 @@
   enum ImageRoot {
     kDexCaches,
     kClassRoots,
-    kOomeWhenThrowingException,       // Pre-allocated OOME when throwing exception.
-    kOomeWhenThrowingOome,            // Pre-allocated OOME when throwing OOME.
-    kOomeWhenHandlingStackOverflow,   // Pre-allocated OOME when handling StackOverflowError.
-    kNoClassDefFoundError,            // Pre-allocated NoClassDefFoundError.
     kSpecialRoots,                    // Different for boot image and app image, see aliases below.
     kImageRootsMax,
 
@@ -234,6 +230,15 @@
     kBootImageLiveObjects = kSpecialRoots,  // Array of boot image objects that must be kept live.
   };
 
+  enum BootImageLiveObjects {
+    kOomeWhenThrowingException,       // Pre-allocated OOME when throwing exception.
+    kOomeWhenThrowingOome,            // Pre-allocated OOME when throwing OOME.
+    kOomeWhenHandlingStackOverflow,   // Pre-allocated OOME when handling StackOverflowError.
+    kNoClassDefFoundError,            // Pre-allocated NoClassDefFoundError.
+    kClearedJniWeakSentinel,          // Pre-allocated sentinel for cleared weak JNI references.
+    kIntrinsicObjectsStart
+  };
+
   /*
    * This describes the number and ordering of sections inside of Boot
    * and App Images.  It is very important that changes to this struct
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index cda1fd9..3ba888f 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1094,7 +1094,7 @@
   return failure_count;
 }
 
-void Runtime::SetSentinel(mirror::Object* sentinel) {
+void Runtime::SetSentinel(ObjPtr<mirror::Object> sentinel) {
   CHECK(sentinel_.Read() == nullptr);
   CHECK(sentinel != nullptr);
   CHECK(!heap_->IsMovableObject(sentinel));
@@ -1612,20 +1612,23 @@
 
   if (GetHeap()->HasBootImageSpace()) {
     const ImageHeader& image_header = GetHeap()->GetBootImageSpaces()[0]->GetImageHeader();
+    ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects =
+        ObjPtr<mirror::ObjectArray<mirror::Object>>::DownCast(
+            image_header.GetImageRoot(ImageHeader::kBootImageLiveObjects));
     pre_allocated_OutOfMemoryError_when_throwing_exception_ = GcRoot<mirror::Throwable>(
-        image_header.GetImageRoot(ImageHeader::kOomeWhenThrowingException)->AsThrowable());
+        boot_image_live_objects->Get(ImageHeader::kOomeWhenThrowingException)->AsThrowable());
     DCHECK(pre_allocated_OutOfMemoryError_when_throwing_exception_.Read()->GetClass()
                ->DescriptorEquals("Ljava/lang/OutOfMemoryError;"));
     pre_allocated_OutOfMemoryError_when_throwing_oome_ = GcRoot<mirror::Throwable>(
-        image_header.GetImageRoot(ImageHeader::kOomeWhenThrowingOome)->AsThrowable());
+        boot_image_live_objects->Get(ImageHeader::kOomeWhenThrowingOome)->AsThrowable());
     DCHECK(pre_allocated_OutOfMemoryError_when_throwing_oome_.Read()->GetClass()
                ->DescriptorEquals("Ljava/lang/OutOfMemoryError;"));
     pre_allocated_OutOfMemoryError_when_handling_stack_overflow_ = GcRoot<mirror::Throwable>(
-        image_header.GetImageRoot(ImageHeader::kOomeWhenHandlingStackOverflow)->AsThrowable());
+        boot_image_live_objects->Get(ImageHeader::kOomeWhenHandlingStackOverflow)->AsThrowable());
     DCHECK(pre_allocated_OutOfMemoryError_when_handling_stack_overflow_.Read()->GetClass()
                ->DescriptorEquals("Ljava/lang/OutOfMemoryError;"));
     pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(
-        image_header.GetImageRoot(ImageHeader::kNoClassDefFoundError)->AsThrowable());
+        boot_image_live_objects->Get(ImageHeader::kNoClassDefFoundError)->AsThrowable());
     DCHECK(pre_allocated_NoClassDefFoundError_.Read()->GetClass()
                ->DescriptorEquals("Ljava/lang/NoClassDefFoundError;"));
   } else {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index dd66b85..7050fd2 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -719,7 +719,7 @@
   }
 
   // Called from class linker.
-  void SetSentinel(mirror::Object* sentinel) REQUIRES_SHARED(Locks::mutator_lock_);
+  void SetSentinel(ObjPtr<mirror::Object> sentinel) REQUIRES_SHARED(Locks::mutator_lock_);
   // For testing purpose only.
   // TODO: Remove this when this is no longer needed (b/116087961).
   GcRoot<mirror::Object> GetSentinel() REQUIRES_SHARED(Locks::mutator_lock_);