Implement Integer.valueOf() intrinsic for boot image.

And generate only one "boot image live objects" array rather
than one per boot*.art file.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: Pixel 2 XL boots.
Test: testrunner.py --target --optimizing
Bug: 71526895
Change-Id: I23af7f47fea5150805f801cd2512f2d152ee5b73
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 81b2b7b..21efe11 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -223,6 +223,13 @@
   return os;
 }
 
+static const char kIntegerCacheDescriptor[] = "Ljava/lang/Integer$IntegerCache;";
+static const char kIntegerDescriptor[] = "Ljava/lang/Integer;";
+static const char kIntegerArrayDescriptor[] = "[Ljava/lang/Integer;";
+static const char kLowFieldName[] = "low";
+static const char kHighFieldName[] = "high";
+static const char kValueFieldName[] = "value";
+
 static ObjPtr<mirror::ObjectArray<mirror::Object>> GetBootImageLiveObjects()
     REQUIRES_SHARED(Locks::mutator_lock_) {
   gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -237,6 +244,31 @@
   return boot_image_live_objects;
 }
 
+static ObjPtr<mirror::Class> LookupInitializedClass(Thread* self,
+                                                    ClassLinker* class_linker,
+                                                    const char* descriptor)
+        REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjPtr<mirror::Class> klass =
+      class_linker->LookupClass(self, descriptor, /* class_loader */ nullptr);
+  DCHECK(klass != nullptr);
+  DCHECK(klass->IsInitialized());
+  return klass;
+}
+
+static ObjPtr<mirror::ObjectArray<mirror::Object>> GetIntegerCacheArray(
+    ObjPtr<mirror::Class> cache_class) REQUIRES_SHARED(Locks::mutator_lock_) {
+  ArtField* cache_field = cache_class->FindDeclaredStaticField("cache", kIntegerArrayDescriptor);
+  DCHECK(cache_field != nullptr);
+  return ObjPtr<mirror::ObjectArray<mirror::Object>>::DownCast(cache_field->GetObject(cache_class));
+}
+
+static int32_t GetIntegerCacheField(ObjPtr<mirror::Class> cache_class, const char* field_name)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ArtField* field = cache_class->FindDeclaredStaticField(field_name, "I");
+  DCHECK(field != nullptr);
+  return field->GetInt(cache_class);
+}
+
 static bool CheckIntegerCache(Thread* self,
                               ClassLinker* class_linker,
                               ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects,
@@ -246,37 +278,26 @@
 
   // Since we have a cache in the boot image, both java.lang.Integer and
   // java.lang.Integer$IntegerCache must be initialized in the boot image.
-  ObjPtr<mirror::Class> cache_class = class_linker->LookupClass(
-      self, "Ljava/lang/Integer$IntegerCache;", /* class_loader */ nullptr);
-  DCHECK(cache_class != nullptr);
-  DCHECK(cache_class->IsInitialized());
+  ObjPtr<mirror::Class> cache_class =
+      LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor);
   ObjPtr<mirror::Class> integer_class =
-      class_linker->LookupClass(self, "Ljava/lang/Integer;", /* class_loader */ nullptr);
-  DCHECK(integer_class != nullptr);
-  DCHECK(integer_class->IsInitialized());
+      LookupInitializedClass(self, class_linker, kIntegerDescriptor);
 
   // Check that the current cache is the same as the `boot_image_cache`.
-  ArtField* cache_field = cache_class->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
-  DCHECK(cache_field != nullptr);
-  ObjPtr<mirror::ObjectArray<mirror::Object>> current_cache =
-      ObjPtr<mirror::ObjectArray<mirror::Object>>::DownCast(cache_field->GetObject(cache_class));
+  ObjPtr<mirror::ObjectArray<mirror::Object>> current_cache = GetIntegerCacheArray(cache_class);
   if (current_cache != boot_image_cache) {
     return false;  // Messed up IntegerCache.cache.
   }
 
   // Check that the range matches the boot image cache length.
-  ArtField* low_field = cache_class->FindDeclaredStaticField("low", "I");
-  DCHECK(low_field != nullptr);
-  int32_t low = low_field->GetInt(cache_class);
-  ArtField* high_field = cache_class->FindDeclaredStaticField("high", "I");
-  DCHECK(high_field != nullptr);
-  int32_t high = high_field->GetInt(cache_class);
+  int32_t low = GetIntegerCacheField(cache_class, kLowFieldName);
+  int32_t high = GetIntegerCacheField(cache_class, kHighFieldName);
   if (boot_image_cache->GetLength() != high - low + 1) {
     return false;  // Messed up IntegerCache.low or IntegerCache.high.
   }
 
   // Check that the elements match the boot image intrinsic objects and check their values as well.
-  ArtField* value_field = integer_class->FindDeclaredInstanceField("value", "I");
+  ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
   DCHECK(value_field != nullptr);
   for (int32_t i = 0, len = boot_image_cache->GetLength(); i != len; ++i) {
     ObjPtr<mirror::Object> boot_image_object =
@@ -300,20 +321,64 @@
                                                       CodeGenerator* codegen,
                                                       Location return_location,
                                                       Location first_argument_location) {
-  if (codegen->GetCompilerOptions().IsBootImage()) {
-    // TODO: Implement for boot image. We need access to CompilerDriver::IsImageClass()
-    // to verify that the IntegerCache shall be in the image.
-    return;
-  }
-  Runtime* runtime = Runtime::Current();
-  gc::Heap* heap = runtime->GetHeap();
-  if (heap->GetBootImageSpaces().empty()) {
-    return;  // Running without boot image, cannot use required boot image objects.
-  }
-
   // The intrinsic will call if it needs to allocate a j.l.Integer.
   LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
-  {
+  const CompilerOptions& compiler_options = codegen->GetCompilerOptions();
+  if (compiler_options.IsBootImage()) {
+    // Piggyback on the method load kind to determine whether we can use PC-relative addressing.
+    // This should cover both the testing config (non-PIC boot image) and codegens that reject
+    // PC-relative load kinds and fall back to the runtime call.
+    if (!invoke->AsInvokeStaticOrDirect()->HasPcRelativeMethodLoadKind()) {
+      return;
+    }
+    if (!compiler_options.IsImageClass(kIntegerCacheDescriptor) ||
+        !compiler_options.IsImageClass(kIntegerDescriptor)) {
+      return;
+    }
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    Thread* self = Thread::Current();
+    ScopedObjectAccess soa(self);
+    ObjPtr<mirror::Class> cache_class = class_linker->LookupClass(
+        self, kIntegerCacheDescriptor, /* class_loader */ nullptr);
+    DCHECK(cache_class != nullptr);
+    if (UNLIKELY(!cache_class->IsInitialized())) {
+      LOG(WARNING) << "Image class " << cache_class->PrettyDescriptor() << " is uninitialized.";
+      return;
+    }
+    ObjPtr<mirror::Class> integer_class =
+        class_linker->LookupClass(self, kIntegerDescriptor, /* class_loader */ nullptr);
+    DCHECK(integer_class != nullptr);
+    if (UNLIKELY(!integer_class->IsInitialized())) {
+      LOG(WARNING) << "Image class " << integer_class->PrettyDescriptor() << " is uninitialized.";
+      return;
+    }
+    int32_t low = GetIntegerCacheField(cache_class, kLowFieldName);
+    int32_t high = GetIntegerCacheField(cache_class, kHighFieldName);
+    if (kIsDebugBuild) {
+      ObjPtr<mirror::ObjectArray<mirror::Object>> current_cache = GetIntegerCacheArray(cache_class);
+      CHECK(current_cache != nullptr);
+      CHECK_EQ(current_cache->GetLength(), high - low + 1);
+      ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
+      CHECK(value_field != nullptr);
+      for (int32_t i = 0, len = current_cache->GetLength(); i != len; ++i) {
+        ObjPtr<mirror::Object> current_object = current_cache->GetWithoutChecks(i);
+        CHECK(current_object != nullptr);
+        CHECK_EQ(value_field->GetInt(current_object), low + i);
+      }
+    }
+    if (invoke->InputAt(0)->IsIntConstant()) {
+      int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
+      if (static_cast<uint32_t>(value) - static_cast<uint32_t>(low) <
+          static_cast<uint32_t>(high - low + 1)) {
+        // No call, we shall use direct pointer to the Integer object.
+        call_kind = LocationSummary::kNoCall;
+      }
+    }
+  } else {
+    Runtime* runtime = Runtime::Current();
+    if (runtime->GetHeap()->GetBootImageSpaces().empty()) {
+      return;  // Running without boot image, cannot use required boot image objects.
+    }
     Thread* self = Thread::Current();
     ScopedObjectAccess soa(self);
     ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects = GetBootImageLiveObjects();
@@ -336,7 +401,7 @@
             IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, 0u);
         ObjPtr<mirror::Class> integer_class =
             low_integer->GetClass<kVerifyNone, kWithoutReadBarrier>();
-        ArtField* value_field = integer_class->FindDeclaredInstanceField("value", "I");
+        ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
         DCHECK(value_field != nullptr);
         int32_t low = value_field->GetInt(low_integer);
         if (static_cast<uint32_t>(value) - static_cast<uint32_t>(low) <
@@ -361,15 +426,11 @@
   }
 }
 
-static int32_t GetIntegerCacheLowFromIntegerCache(Thread* self)
+static int32_t GetIntegerCacheLowFromIntegerCache(Thread* self, ClassLinker* class_linker)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  ObjPtr<mirror::Class> cache_class = Runtime::Current()->GetClassLinker()->LookupClass(
-      self, "Ljava/lang/Integer$IntegerCache;", /* class_loader */ nullptr);
-  DCHECK(cache_class != nullptr);
-  DCHECK(cache_class->IsInitialized());
-  ArtField* low_field = cache_class->FindDeclaredStaticField("low", "I");
-  DCHECK(low_field != nullptr);
-  return low_field->GetInt(cache_class);
+  ObjPtr<mirror::Class> cache_class =
+      LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor);
+  return GetIntegerCacheField(cache_class, kLowFieldName);
 }
 
 static uint32_t CalculateBootImageOffset(ObjPtr<mirror::Object> object)
@@ -380,13 +441,14 @@
 }
 
 inline IntrinsicVisitor::IntegerValueOfInfo::IntegerValueOfInfo()
-    : integer_boot_image_offset(0u),
-      value_offset(0),
+    : value_offset(0),
       low(0),
       length(0u),
-      value_boot_image_offset(0u) {}
+      integer_boot_image_offset(kInvalidReference),
+      value_boot_image_reference(kInvalidReference) {}
 
-IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo(HInvoke* invoke) {
+IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo(
+    HInvoke* invoke, const CompilerOptions& compiler_options) {
   // Note that we could cache all of the data looked up here. but there's no good
   // location for it. We don't want to add it to WellKnownClasses, to avoid creating global
   // jni values. Adding it as state to the compiler singleton seems like wrong
@@ -398,46 +460,76 @@
   // modified through reflection since ComputeIntegerValueOfLocations() when JITting.
 
   Runtime* runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
-  ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects = GetBootImageLiveObjects();
-  ObjPtr<mirror::Object> low_integer =
-      IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, 0u);
-  ObjPtr<mirror::Class> integer_class = low_integer->GetClass<kVerifyNone, kWithoutReadBarrier>();
-  ArtField* value_field = integer_class->FindDeclaredInstanceField("value", "I");
-  DCHECK(value_field != nullptr);
 
   IntegerValueOfInfo info;
-  info.integer_boot_image_offset = CalculateBootImageOffset(integer_class);
-  info.value_offset = value_field->GetOffset().Uint32Value();
-  if (runtime->UseJitCompilation()) {
-    // Use the current `IntegerCache.low` for JIT to avoid truly surprising behavior if the
-    // code messes up the `value` field in the lowest cached Integer using reflection.
-    info.low = GetIntegerCacheLowFromIntegerCache(self);
-  } else {
-    // For AOT, the `low_integer->value` should be the same as `IntegerCache.low`.
-    info.low = value_field->GetInt(low_integer);
-    DCHECK_EQ(info.low, GetIntegerCacheLowFromIntegerCache(self));
-  }
-  // Do not look at `IntegerCache.high`, use the immutable length of the cache array instead.
-  info.length = dchecked_integral_cast<uint32_t>(
-      IntrinsicObjects::GetIntegerValueOfCache(boot_image_live_objects)->GetLength());
+  if (compiler_options.IsBootImage()) {
+    ObjPtr<mirror::Class> integer_class =
+        LookupInitializedClass(self, class_linker, kIntegerDescriptor);
+    ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
+    DCHECK(value_field != nullptr);
+    info.value_offset = value_field->GetOffset().Uint32Value();
+    ObjPtr<mirror::Class> cache_class =
+        LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor);
+    info.low = GetIntegerCacheField(cache_class, kLowFieldName);
+    int32_t high = GetIntegerCacheField(cache_class, kHighFieldName);
+    info.length = dchecked_integral_cast<uint32_t>(high - info.low + 1);
 
-  if (invoke->InputAt(0)->IsIntConstant()) {
-    int32_t input_value = invoke->InputAt(0)->AsIntConstant()->GetValue();
-    uint32_t index = static_cast<uint32_t>(input_value) - static_cast<uint32_t>(info.low);
-    if (index < static_cast<uint32_t>(info.length)) {
-      ObjPtr<mirror::Object> integer =
-          IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, index);
-      DCHECK(runtime->GetHeap()->ObjectIsInBootImageSpace(integer));
-      info.value_boot_image_offset = CalculateBootImageOffset(integer);
+    info.integer_boot_image_offset = IntegerValueOfInfo::kInvalidReference;
+    if (invoke->InputAt(0)->IsIntConstant()) {
+      int32_t input_value = invoke->InputAt(0)->AsIntConstant()->GetValue();
+      uint32_t index = static_cast<uint32_t>(input_value) - static_cast<uint32_t>(info.low);
+      if (index < static_cast<uint32_t>(info.length)) {
+        info.value_boot_image_reference = IntrinsicObjects::EncodePatch(
+            IntrinsicObjects::PatchType::kIntegerValueOfObject, index);
+      } else {
+        // Not in the cache.
+        info.value_boot_image_reference = IntegerValueOfInfo::kInvalidReference;
+      }
     } else {
-      info.value_boot_image_offset = 0u;  // Not in the cache.
+      info.array_data_boot_image_reference =
+          IntrinsicObjects::EncodePatch(IntrinsicObjects::PatchType::kIntegerValueOfArray);
     }
   } else {
-    info.array_data_boot_image_offset =
-        CalculateBootImageOffset(boot_image_live_objects) +
-        IntrinsicObjects::GetIntegerValueOfArrayDataOffset(boot_image_live_objects).Uint32Value();
+    ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects = GetBootImageLiveObjects();
+    ObjPtr<mirror::Object> low_integer =
+        IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, 0u);
+    ObjPtr<mirror::Class> integer_class = low_integer->GetClass<kVerifyNone, kWithoutReadBarrier>();
+    ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
+    DCHECK(value_field != nullptr);
+    info.value_offset = value_field->GetOffset().Uint32Value();
+    if (runtime->UseJitCompilation()) {
+      // Use the current `IntegerCache.low` for JIT to avoid truly surprising behavior if the
+      // code messes up the `value` field in the lowest cached Integer using reflection.
+      info.low = GetIntegerCacheLowFromIntegerCache(self, class_linker);
+    } else {
+      // For app AOT, the `low_integer->value` should be the same as `IntegerCache.low`.
+      info.low = value_field->GetInt(low_integer);
+      DCHECK_EQ(info.low, GetIntegerCacheLowFromIntegerCache(self, class_linker));
+    }
+    // Do not look at `IntegerCache.high`, use the immutable length of the cache array instead.
+    info.length = dchecked_integral_cast<uint32_t>(
+        IntrinsicObjects::GetIntegerValueOfCache(boot_image_live_objects)->GetLength());
+
+    info.integer_boot_image_offset = CalculateBootImageOffset(integer_class);
+    if (invoke->InputAt(0)->IsIntConstant()) {
+      int32_t input_value = invoke->InputAt(0)->AsIntConstant()->GetValue();
+      uint32_t index = static_cast<uint32_t>(input_value) - static_cast<uint32_t>(info.low);
+      if (index < static_cast<uint32_t>(info.length)) {
+        ObjPtr<mirror::Object> integer =
+            IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, index);
+        info.value_boot_image_reference = CalculateBootImageOffset(integer);
+      } else {
+        // Not in the cache.
+        info.value_boot_image_reference = IntegerValueOfInfo::kInvalidReference;
+      }
+    } else {
+      info.array_data_boot_image_reference =
+          CalculateBootImageOffset(boot_image_live_objects) +
+          IntrinsicObjects::GetIntegerValueOfArrayDataOffset(boot_image_live_objects).Uint32Value();
+    }
   }
 
   return info;