ART: Add compiled-methods

Add a dex2oat option for compiled-methods, a more granular filter
than compiled-classes. Add compiler-driver support for it.

Refactor dex2oat to reuse file reading.

Add a test to oat_test.

Change-Id: I78d0d040bce7738b4bb7aabe7768b5788d2587ac
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 8ffc86e..05cb8b4 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -140,6 +140,27 @@
   }
 }
 
+// Get the set of image classes given to the compiler-driver in SetUp. Note: the compiler
+// driver assumes ownership of the set, so the test should properly release the set.
+std::unordered_set<std::string>* CommonCompilerTest::GetImageClasses() {
+  // Empty set: by default no classes are retained in the image.
+  return new std::unordered_set<std::string>();
+}
+
+// Get the set of compiled classes given to the compiler-driver in SetUp. Note: the compiler
+// driver assumes ownership of the set, so the test should properly release the set.
+std::unordered_set<std::string>* CommonCompilerTest::GetCompiledClasses() {
+  // Null, no selection of compiled-classes.
+  return nullptr;
+}
+
+// Get the set of compiled methods given to the compiler-driver in SetUp. Note: the compiler
+// driver assumes ownership of the set, so the test should properly release the set.
+std::unordered_set<std::string>* CommonCompilerTest::GetCompiledMethods() {
+  // Null, no selection of compiled-methods.
+  return nullptr;
+}
+
 void CommonCompilerTest::SetUp() {
   CommonRuntimeTest::SetUp();
   {
@@ -165,7 +186,10 @@
                                               method_inliner_map_.get(),
                                               compiler_kind, instruction_set,
                                               instruction_set_features_.get(),
-                                              true, new std::unordered_set<std::string>, nullptr,
+                                              true,
+                                              GetImageClasses(),
+                                              GetCompiledClasses(),
+                                              GetCompiledMethods(),
                                               2, true, true, "", timer_.get(), -1, ""));
   }
   // We typically don't generate an image in unit tests, disable this optimization by default.
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index d7b210d..8d80a2d 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_COMMON_COMPILER_TEST_H_
 
 #include <list>
+#include <unordered_set>
 #include <vector>
 
 #include "common_runtime_test.h"
@@ -56,6 +57,18 @@
 
   virtual void SetUpRuntimeOptions(RuntimeOptions *options);
 
+  // Get the set of image classes given to the compiler-driver in SetUp. Note: the compiler
+  // driver assumes ownership of the set, so the test should properly release the set.
+  virtual std::unordered_set<std::string>* GetImageClasses();
+
+  // Get the set of compiled classes given to the compiler-driver in SetUp. Note: the compiler
+  // driver assumes ownership of the set, so the test should properly release the set.
+  virtual std::unordered_set<std::string>* GetCompiledClasses();
+
+  // Get the set of compiled methods given to the compiler-driver in SetUp. Note: the compiler
+  // driver assumes ownership of the set, so the test should properly release the set.
+  virtual std::unordered_set<std::string>* GetCompiledMethods();
+
   virtual void TearDown();
 
   void CompileClass(mirror::ClassLoader* class_loader, const char* class_name)
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index d276457..555d5b9 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -76,7 +76,7 @@
     isa_features.reset(InstructionSetFeatures::FromVariant(isa, "default", &error));
     CompilerDriver driver(&compiler_options, &verification_results, &method_inliner_map,
                           Compiler::kQuick, isa, isa_features.get(),
-                          false, 0, 0, 0, false, false, "", 0, -1, "");
+                          false, nullptr, nullptr, nullptr, 0, false, false, "", 0, -1, "");
     ClassLinker* linker = nullptr;
     CompilationUnit cu(&pool, isa, &driver, linker);
     DexFile::CodeItem code_item { 0, 0, 0, 0, 0, 0, { 0 } };  // NOLINT
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 1832647..e665e1d 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -76,8 +76,8 @@
 // Whether to produce 64-bit ELF files for 64-bit targets. Leave this off for now.
 static constexpr bool kProduce64BitELFFiles = false;
 
-// Whether classes-to-compile is only applied to the boot image, or, when given, too all
-// compilations.
+// Whether classes-to-compile and methods-to-compile are only applied to the boot image, or, when
+// given, too all compilations.
 static constexpr bool kRestrictCompilationFiltersToImage = true;
 
 static double Percentage(size_t x, size_t y) {
@@ -349,6 +349,7 @@
                                const InstructionSetFeatures* instruction_set_features,
                                bool image, std::unordered_set<std::string>* image_classes,
                                std::unordered_set<std::string>* compiled_classes,
+                               std::unordered_set<std::string>* compiled_methods,
                                size_t thread_count, bool dump_stats, bool dump_passes,
                                const std::string& dump_cfg_file_name, CumulativeLogger* timer,
                                int swap_fd, const std::string& profile_file)
@@ -369,6 +370,7 @@
       image_(image),
       image_classes_(image_classes),
       classes_to_compile_(compiled_classes),
+      methods_to_compile_(compiled_methods),
       had_hard_verifier_failure_(false),
       thread_count_(thread_count),
       stats_(new AOTCompilationStats),
@@ -670,6 +672,19 @@
   return classes_to_compile_->find(descriptor) != classes_to_compile_->end();
 }
 
+bool CompilerDriver::IsMethodToCompile(const MethodReference& method_ref) const {
+  if (kRestrictCompilationFiltersToImage && !IsImage()) {
+    return true;
+  }
+
+  if (methods_to_compile_ == nullptr) {
+    return true;
+  }
+
+  std::string tmp = PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file, true);
+  return methods_to_compile_->find(tmp.c_str()) != methods_to_compile_->end();
+}
+
 static void ResolveExceptionsForMethod(MutableHandle<mirror::ArtMethod> method_handle,
     std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -2232,7 +2247,9 @@
                    // Basic checks, e.g., not <clinit>.
                    verification_results_->IsCandidateForCompilation(method_ref, access_flags) &&
                    // Did not fail to create VerifiedMethod metadata.
-                   has_verified_method;
+                   has_verified_method &&
+                   // Is eligable for compilation by methods-to-compile filter.
+                   IsMethodToCompile(method_ref);
     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 ce13a17..50e1fb1 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -104,6 +104,7 @@
                           const InstructionSetFeatures* instruction_set_features,
                           bool image, std::unordered_set<std::string>* image_classes,
                           std::unordered_set<std::string>* compiled_classes,
+                          std::unordered_set<std::string>* compiled_methods,
                           size_t thread_count, bool dump_stats, bool dump_passes,
                           const std::string& dump_cfg_file_name,
                           CumulativeLogger* timer, int swap_fd,
@@ -428,6 +429,9 @@
   // Checks whether the provided class should be compiled, i.e., is in classes_to_compile_.
   bool IsClassToCompile(const char* descriptor) const;
 
+  // Checks whether the provided method should be compiled, i.e., is in method_to_compile_.
+  bool IsMethodToCompile(const MethodReference& method_ref) const;
+
   void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
       LOCKS_EXCLUDED(compiled_classes_lock_);
 
@@ -597,6 +601,11 @@
   // This option may be restricted to the boot image, depending on a flag in the implementation.
   std::unique_ptr<std::unordered_set<std::string>> classes_to_compile_;
 
+  // Specifies the methods that will be compiled. Note that if methods_to_compile_ is nullptr,
+  // all methods are eligible for compilation (compilation filters etc. will still apply).
+  // This option may be restricted to the boot image, depending on a flag in the implementation.
+  std::unique_ptr<std::unordered_set<std::string>> methods_to_compile_;
+
   bool had_hard_verifier_failure_;
 
   size_t thread_count_;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index e78ff90..ded50ca 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -175,6 +175,60 @@
   }
 }
 
+class CompilerDriverMethodsTest : public CompilerDriverTest {
+ protected:
+  std::unordered_set<std::string>* GetCompiledMethods() OVERRIDE {
+    return new std::unordered_set<std::string>({
+      "byte StaticLeafMethods.identity(byte)",
+      "int StaticLeafMethods.sum(int, int, int)",
+      "double StaticLeafMethods.sum(double, double, double, double)"
+    });
+  }
+};
+
+TEST_F(CompilerDriverMethodsTest, Selection) {
+  Thread* self = Thread::Current();
+  jobject class_loader;
+  {
+    ScopedObjectAccess soa(self);
+    class_loader = LoadDex("StaticLeafMethods");
+  }
+  ASSERT_NE(class_loader, nullptr);
+
+  // Need to enable dex-file writability. Methods rejected to be compiled will run through the
+  // dex-to-dex compiler.
+  for (const DexFile* dex_file : GetDexFiles(class_loader)) {
+    ASSERT_TRUE(dex_file->EnableWrite());
+  }
+
+  CompileAll(class_loader);
+
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  StackHandleScope<1> hs(self);
+  ScopedObjectAccess soa(self);
+  Handle<mirror::ClassLoader> h_loader(hs.NewHandle(
+      reinterpret_cast<mirror::ClassLoader*>(self->DecodeJObject(class_loader))));
+  mirror::Class* klass = class_linker->FindClass(self, "LStaticLeafMethods;", h_loader);
+  ASSERT_NE(klass, nullptr);
+
+  std::unique_ptr<std::unordered_set<std::string>> expected(GetCompiledMethods());
+
+  for (int32_t i = 0; static_cast<uint32_t>(i) < klass->NumDirectMethods(); i++) {
+    mirror::ArtMethod* m = klass->GetDirectMethod(i);
+    std::string name = PrettyMethod(m, true);
+    const void* code =
+        m->GetEntryPointFromQuickCompiledCodePtrSize(InstructionSetPointerSize(kRuntimeISA));
+    ASSERT_NE(code, nullptr);
+    if (expected->find(name) != expected->end()) {
+      expected->erase(name);
+      EXPECT_FALSE(class_linker->IsQuickToInterpreterBridge(code));
+    } else {
+      EXPECT_TRUE(class_linker->IsQuickToInterpreterBridge(code));
+    }
+  }
+  EXPECT_TRUE(expected->empty());
+}
+
 // TODO: need check-cast test (when stub complete & we can throw/catch
 
 }  // namespace art
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 9ff7ab8..6a08548 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -94,7 +94,7 @@
   compiler_driver_.reset(new CompilerDriver(
       compiler_options_.get(), verification_results_.get(), method_inliner_map_.get(),
       Compiler::kQuick, instruction_set, instruction_set_features_.get(), false,
-      nullptr, nullptr, 1, false, true,
+      nullptr, nullptr, nullptr, 1, false, true,
       std::string(), cumulative_logger_.get(), -1, std::string()));
   // Disable dedupe so we can remove compiled methods.
   compiler_driver_->SetDedupeEnabled(false);
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index 70630f3..1f7500a 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -45,7 +45,7 @@
         inliner_map_(),
         driver_(&compiler_options_, &verification_results_, &inliner_map_,
                 Compiler::kQuick, instruction_set, nullptr,
-                false, nullptr, nullptr, 1u,
+                false, nullptr, nullptr, nullptr, 1u,
                 false, false, "", nullptr, -1, ""),
         error_msg_(),
         instruction_set_(instruction_set),
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 989b04f..925b507 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -93,8 +93,8 @@
                                             verification_results_.get(),
                                             method_inliner_map_.get(),
                                             compiler_kind, insn_set,
-                                            insn_features.get(), false, nullptr, nullptr, 2, true,
-                                            true, "", timer_.get(), -1, ""));
+                                            insn_features.get(), false, nullptr, nullptr, nullptr,
+                                            2, true, true, "", timer_.get(), -1, ""));
   jobject class_loader = nullptr;
   if (kCompile) {
     TimingLogger timings2("OatTest::WriteRead", false, false);