Resolve const-string of clinit

Resolve the const strings of the clinit for classes that are loaded in the profile.

For Maps, the number of strings is ~4400. This would take ~100k RAM if each string
is around 20 characters. Note that these strings are mostly already loaded during
startup, so there might not be a net RAM usage increase.

Test: test-art-host
Bug: 117621117

Change-Id: I632a91633c1b3757523ef6e655f192305554499b
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 89ac308..7690e23 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -715,6 +715,7 @@
   StackHandleScope<1> hs(soa.Self());
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
   MutableHandle<mirror::DexCache> dex_cache(hs.NewHandle<mirror::DexCache>(nullptr));
+  size_t num_instructions = 0u;
 
   for (const DexFile* dex_file : dex_files) {
     dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file));
@@ -726,10 +727,20 @@
         // FIXME: Make sure that inlining honors this. b/26687569
         continue;
       }
+
+      const bool is_startup_class =
+          profile_compilation_info_ != nullptr &&
+          profile_compilation_info_->ContainsClass(*dex_file, accessor.GetClassIdx());
+
       for (const ClassAccessor::Method& method : accessor.GetMethods()) {
+        const bool is_clinit = (method.GetAccessFlags() & kAccConstructor) != 0 &&
+            (method.GetAccessFlags() & kAccStatic) != 0;
+        const bool is_startup_clinit = is_startup_class && is_clinit;
+
         if (only_startup_strings &&
             profile_compilation_info_ != nullptr &&
-            !profile_compilation_info_->GetMethodHotness(method.GetReference()).IsStartup()) {
+            (!profile_compilation_info_->GetMethodHotness(method.GetReference()).IsStartup() &&
+             !is_startup_clinit)) {
           continue;
         }
 
@@ -746,6 +757,7 @@
                   : inst->VRegB_31c());
               ObjPtr<mirror::String> string = class_linker->ResolveString(string_index, dex_cache);
               CHECK(string != nullptr) << "Could not allocate a string when forcing determinism";
+              ++num_instructions;
               break;
             }
 
@@ -756,6 +768,7 @@
       }
     }
   }
+  VLOG(compiler) << "Resolved " << num_instructions << " const string instructions";
 }
 
 // Initialize type check bit strings for check-cast and instance-of in the code. Done to have
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index b945b2a..c9bd6d2 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -2091,16 +2091,25 @@
   // Create a profile with the startup method marked.
   ScratchFile profile_file;
   std::vector<uint16_t> methods;
+  std::vector<dex::TypeIndex> classes;
   {
     std::unique_ptr<const DexFile> dex(OpenTestDexFile("StringLiterals"));
-    for (size_t method_idx = 0; method_idx < dex->NumMethodIds(); ++method_idx) {
-      if (std::string(dex->GetMethodName(dex->GetMethodId(method_idx))) == "startUpMethod") {
-        methods.push_back(method_idx);
+    for (ClassAccessor accessor : dex->GetClasses()) {
+      if (accessor.GetDescriptor() == std::string("LStringLiterals$StartupClass;")) {
+        classes.push_back(accessor.GetClassIdx());
+      }
+      for (const ClassAccessor::Method& method : accessor.GetMethods()) {
+        std::string method_name(dex->GetMethodName(dex->GetMethodId(method.GetIndex())));
+        if (method_name == "startUpMethod") {
+          methods.push_back(method.GetIndex());
+        }
       }
     }
+    ASSERT_GT(classes.size(), 0u);
     ASSERT_GT(methods.size(), 0u);
     // Here, we build the profile from the method lists.
     ProfileCompilationInfo info;
+    info.AddClassesForDex(dex.get(), classes.begin(), classes.end());
     info.AddMethodsForDex(Hotness::kFlagStartup, dex.get(), methods.begin(), methods.end());
     // Save the profile since we want to use it with dex2oat to produce an oat file.
     ASSERT_TRUE(info.Save(profile_file.GetFd()));
@@ -2145,11 +2154,15 @@
         seen.insert(str.Read()->ToModifiedUtf8());
       }
     });
+    // Normal methods
     EXPECT_TRUE(seen.find("Loading ") != seen.end());
     EXPECT_TRUE(seen.find("Starting up") != seen.end());
     EXPECT_TRUE(seen.find("abcd.apk") != seen.end());
     EXPECT_TRUE(seen.find("Unexpected error") == seen.end());
     EXPECT_TRUE(seen.find("Shutting down!") == seen.end());
+    // Classes initializers
+    EXPECT_TRUE(seen.find("Startup init") != seen.end());
+    EXPECT_TRUE(seen.find("Other class init") == seen.end());
   }
 }
 
diff --git a/test/StringLiterals/StringLiterals.java b/test/StringLiterals/StringLiterals.java
index 8dee666..9ab37ca 100644
--- a/test/StringLiterals/StringLiterals.java
+++ b/test/StringLiterals/StringLiterals.java
@@ -15,6 +15,18 @@
  */
 
 class StringLiterals {
+    static class StartupClass {
+        static {
+            System.out.println("Startup init");
+        }
+    }
+
+    static class OtherClass {
+        static {
+            System.out.println("Other class init");
+        }
+    }
+
     void startUpMethod() {
         String resource = "abcd.apk";
         System.out.println("Starting up");