ART: Compiled-classes list for compiler-driver

Similar to the image-classes list, introduce a list of class names
that are to be compiled when creating a boot image. This defaults
to all classes.

Bug: 18336591

(cherry picked from commit 26318f722958ac1cba6a812026a1377f37c54941)

Change-Id: I95f69afdb500a9defb6795803d4040bbe67c5a01
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 86167ec..5e4623d 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -321,7 +321,7 @@
                                               method_inliner_map_.get(),
                                               compiler_kind, instruction_set,
                                               instruction_set_features,
-                                              true, new std::set<std::string>,
+                                              true, new std::set<std::string>, nullptr,
                                               2, true, true, timer_.get()));
   }
   // We typically don't generate an image in unit tests, disable this optimization by default.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 9a44ade..62d70e9 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -330,7 +330,8 @@
                                Compiler::Kind compiler_kind,
                                InstructionSet instruction_set,
                                InstructionSetFeatures instruction_set_features,
-                               bool image, std::set<std::string>* image_classes, size_t thread_count,
+                               bool image, std::set<std::string>* image_classes,
+                               std::set<std::string>* compiled_classes, size_t thread_count,
                                bool dump_stats, bool dump_passes, CumulativeLogger* timer,
                                std::string profile_file)
     : profile_present_(false), compiler_options_(compiler_options),
@@ -344,6 +345,7 @@
       compiled_methods_lock_("compiled method lock"),
       image_(image),
       image_classes_(image_classes),
+      classes_to_compile_(compiled_classes),
       thread_count_(thread_count),
       start_ns_(0),
       stats_(new AOTCompilationStats),
@@ -585,7 +587,7 @@
                                                                class_def);
   }
   CompileMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx, jclass_loader,
-                *dex_file, dex_to_dex_compilation_level);
+                *dex_file, dex_to_dex_compilation_level, true);
 
   self->GetJniEnv()->DeleteGlobalRef(jclass_loader);
 
@@ -628,6 +630,17 @@
   }
 }
 
+bool CompilerDriver::IsClassToCompile(const char* descriptor) const {
+  if (!IsImage()) {
+    return true;
+  } else {
+    if (classes_to_compile_ == nullptr) {
+      return true;
+    }
+    return classes_to_compile_->find(descriptor) != classes_to_compile_->end();
+  }
+}
+
 static void ResolveExceptionsForMethod(MethodHelper* mh,
     std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -1956,6 +1969,10 @@
     it.Next();
   }
   CompilerDriver* driver = manager->GetCompiler();
+
+  bool compilation_enabled = driver->IsClassToCompile(
+      dex_file.StringByTypeIdx(class_def.class_idx_));
+
   // Compile direct methods
   int64_t previous_direct_method_idx = -1;
   while (it.HasNextDirectMethod()) {
@@ -1969,7 +1986,8 @@
     previous_direct_method_idx = method_idx;
     driver->CompileMethod(it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
                           it.GetMethodInvokeType(class_def), class_def_index,
-                          method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level);
+                          method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
+                          compilation_enabled);
     it.Next();
   }
   // Compile virtual methods
@@ -1985,7 +2003,8 @@
     previous_virtual_method_idx = method_idx;
     driver->CompileMethod(it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
                           it.GetMethodInvokeType(class_def), class_def_index,
-                          method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level);
+                          method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
+                          compilation_enabled);
     it.Next();
   }
   DCHECK(!it.HasNext());
@@ -2004,7 +2023,8 @@
                                    InvokeType invoke_type, uint16_t class_def_idx,
                                    uint32_t method_idx, jobject class_loader,
                                    const DexFile& dex_file,
-                                   DexToDexCompilationLevel dex_to_dex_compilation_level) {
+                                   DexToDexCompilationLevel dex_to_dex_compilation_level,
+                                   bool compilation_enabled) {
   CompiledMethod* compiled_method = nullptr;
   uint64_t start_ns = kTimeCompileMethod ? NanoTime() : 0;
 
@@ -2020,7 +2040,8 @@
   } else if ((access_flags & kAccAbstract) != 0) {
   } else {
     MethodReference method_ref(&dex_file, method_idx);
-    bool compile = verification_results_->IsCandidateForCompilation(method_ref, access_flags);
+    bool compile = compilation_enabled &&
+                   verification_results_->IsCandidateForCompilation(method_ref, access_flags);
     if (compile) {
       // NOTE: if compiler declines to compile this method, it will return nullptr.
       compiled_method = compiler_->Compile(code_item, access_flags, invoke_type, class_def_idx,
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 598e196..c487e42 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -104,6 +104,7 @@
                           InstructionSet instruction_set,
                           InstructionSetFeatures instruction_set_features,
                           bool image, std::set<std::string>* image_classes,
+                          std::set<std::string>* compiled_classes,
                           size_t thread_count, bool dump_stats, bool dump_passes,
                           CumulativeLogger* timer, std::string profile_file = "");
 
@@ -588,6 +589,9 @@
   // Checks if class specified by type_idx is one of the image_classes_
   bool IsImageClass(const char* descriptor) const;
 
+  // Checks if the provided class should be compiled, i.e., is in classes_to_compile_.
+  bool IsClassToCompile(const char* descriptor) const;
+
   void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
       LOCKS_EXCLUDED(compiled_classes_lock_);
 
@@ -696,7 +700,8 @@
   void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
                      InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx,
                      jobject class_loader, const DexFile& dex_file,
-                     DexToDexCompilationLevel dex_to_dex_compilation_level)
+                     DexToDexCompilationLevel dex_to_dex_compilation_level,
+                     bool compilation_enabled)
       LOCKS_EXCLUDED(compiled_methods_lock_);
 
   static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
@@ -736,6 +741,11 @@
   // included in the image.
   std::unique_ptr<std::set<std::string>> image_classes_;
 
+  // If image_ is true, specifies the classes that will be compiled in
+  // the image. Note if classes_to_compile_ is nullptr, all classes are
+  // included in the image.
+  std::unique_ptr<std::set<std::string>> classes_to_compile_;
+
   size_t thread_count_;
   uint64_t start_ns_;
 
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 7c1f6c5..c94fc30 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -105,7 +105,7 @@
                                             verification_results_.get(),
                                             method_inliner_map_.get(),
                                             compiler_kind, insn_set,
-                                            insn_features, false, NULL, 2, true, true,
+                                            insn_features, false, NULL, nullptr, 2, true, true,
                                             timer_.get()));
   jobject class_loader = NULL;
   if (kCompile) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 833a678..853f103 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -353,6 +353,7 @@
                                       const std::string& bitcode_filename,
                                       bool image,
                                       std::unique_ptr<std::set<std::string>>& image_classes,
+                                      std::unique_ptr<std::set<std::string>>& compiled_classes,
                                       bool dump_stats,
                                       bool dump_passes,
                                       TimingLogger& timings,
@@ -387,6 +388,7 @@
                                                               instruction_set_features_,
                                                               image,
                                                               image_classes.release(),
+                                                              compiled_classes.release(),
                                                               thread_count_,
                                                               dump_stats,
                                                               dump_passes,
@@ -840,6 +842,8 @@
   std::string bitcode_filename;
   const char* image_classes_zip_filename = nullptr;
   const char* image_classes_filename = nullptr;
+  const char* compiled_classes_zip_filename = nullptr;
+  const char* compiled_classes_filename = nullptr;
   std::string image_filename;
   std::string boot_image_filename;
   uintptr_t image_base = 0;
@@ -939,6 +943,10 @@
       image_classes_filename = option.substr(strlen("--image-classes=")).data();
     } else if (option.starts_with("--image-classes-zip=")) {
       image_classes_zip_filename = option.substr(strlen("--image-classes-zip=")).data();
+    } else if (option.starts_with("--compiled-classes=")) {
+      compiled_classes_filename = option.substr(strlen("--compiled-classes=")).data();
+    } else if (option.starts_with("--compiled-classes-zip=")) {
+      compiled_classes_zip_filename = option.substr(strlen("--compiled-classes-zip=")).data();
     } else if (option.starts_with("--base=")) {
       const char* image_base_str = option.substr(strlen("--base=")).data();
       char* end;
@@ -1120,6 +1128,18 @@
     Usage("--image-classes-zip should be used with --image-classes");
   }
 
+  if (compiled_classes_filename != nullptr && !image) {
+    Usage("--compiled-classes should only be used with --image");
+  }
+
+  if (compiled_classes_filename != nullptr && !boot_image_option.empty()) {
+    Usage("--compiled-classes should not be used with --boot-image");
+  }
+
+  if (compiled_classes_zip_filename != nullptr && compiled_classes_filename == nullptr) {
+    Usage("--compiled-classes-zip should be used with --compiled-classes");
+  }
+
   if (dex_filenames.empty() && zip_fd == -1) {
     Usage("Input must be supplied with either --dex-file or --zip-fd");
   }
@@ -1326,6 +1346,27 @@
   } else if (image) {
     image_classes.reset(new std::set<std::string>);
   }
+  // If --compiled-classes was specified, calculate the full list of classes to compile in the
+  // image.
+  std::unique_ptr<std::set<std::string>> compiled_classes(nullptr);
+  if (compiled_classes_filename != nullptr) {
+    std::string error_msg;
+    if (compiled_classes_zip_filename != nullptr) {
+      compiled_classes.reset(dex2oat->ReadImageClassesFromZip(compiled_classes_zip_filename,
+                                                              compiled_classes_filename,
+                                                              &error_msg));
+    } else {
+      compiled_classes.reset(dex2oat->ReadImageClassesFromFile(compiled_classes_filename));
+    }
+    if (compiled_classes.get() == nullptr) {
+      LOG(ERROR) << "Failed to create list of compiled classes from '" << compiled_classes_filename
+                 << "': " << error_msg;
+      timings.EndTiming();
+      return EXIT_FAILURE;
+    }
+  } else if (image) {
+    compiled_classes.reset(nullptr);  // By default compile everything.
+  }
 
   std::vector<const DexFile*> dex_files;
   if (boot_image_option.empty()) {
@@ -1427,6 +1468,7 @@
                                                                         bitcode_filename,
                                                                         image,
                                                                         image_classes,
+                                                                        compiled_classes,
                                                                         dump_stats,
                                                                         dump_passes,
                                                                         timings,