Add verify-profile compiler filter

Only verifies and dex2dex compiles classes in the profile. Goal
is to reduce application launch time.

~2x faster than interpret-only for Facebook.

Bug: 27688727
Change-Id: Ic9979c4f7ba4930298d0983c4e3c70c63500213f
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5d8e3ba..de000b0 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -382,6 +382,9 @@
 
   compiler_->Init();
 
+  if (compiler_options->VerifyOnlyProfile()) {
+    CHECK(profile_compilation_info_ != nullptr) << "Requires profile";
+  }
   if (boot_image_) {
     CHECK(image_classes_.get() != nullptr) << "Expected image classes for boot image";
   }
@@ -829,10 +832,12 @@
 
   const bool verification_enabled = compiler_options_->IsVerificationEnabled();
   const bool never_verify = compiler_options_->NeverVerify();
+  const bool verify_only_profile = compiler_options_->VerifyOnlyProfile();
 
   // We need to resolve for never_verify since it needs to run dex to dex to add the
   // RETURN_VOID_NO_BARRIER.
-  if (never_verify || verification_enabled) {
+  // Let the verifier resolve as needed for the verify_only_profile case.
+  if ((never_verify || verification_enabled) && !verify_only_profile) {
     Resolve(class_loader, dex_files, timings);
     VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
   }
@@ -917,6 +922,22 @@
   return result;
 }
 
+bool CompilerDriver::ShouldVerifyClassBasedOnProfile(const DexFile& dex_file,
+                                                     uint16_t class_idx) const {
+  if (!compiler_options_->VerifyOnlyProfile()) {
+    // No profile, verify everything.
+    return true;
+  }
+  DCHECK(profile_compilation_info_ != nullptr);
+  bool result = profile_compilation_info_->ContainsClass(dex_file, class_idx);
+  if (kDebugProfileGuidedCompilation) {
+    LOG(INFO) << "[ProfileGuidedCompilation] "
+        << (result ? "Verified" : "Skipped") << " method:"
+        << dex_file.GetClassDescriptor(dex_file.GetClassDef(class_idx));
+  }
+  return result;
+}
+
 class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
  public:
   ResolveCatchBlockExceptionsClassVisitor(
@@ -2196,6 +2217,10 @@
     ATRACE_CALL();
     ScopedObjectAccess soa(Thread::Current());
     const DexFile& dex_file = *manager_->GetDexFile();
+    if (!manager_->GetCompiler()->ShouldVerifyClassBasedOnProfile(dex_file, class_def_index)) {
+      // Skip verification since the class is not in the profile.
+      return;
+    }
     const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
     const char* descriptor = dex_file.GetClassDescriptor(class_def);
     ClassLinker* class_linker = manager_->GetClassLinker();
@@ -2661,6 +2686,7 @@
       case mirror::Class::kStatusRetryVerificationAtRuntime:
       case mirror::Class::kStatusVerified:
       case mirror::Class::kStatusInitialized:
+      case mirror::Class::kStatusResolved:
         break;  // Expected states.
       default:
         LOG(FATAL) << "Unexpected class status for class "
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 42a5bc1..e40f7c0 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -91,7 +91,8 @@
                  Compiler::Kind compiler_kind,
                  InstructionSet instruction_set,
                  const InstructionSetFeatures* instruction_set_features,
-                 bool boot_image, std::unordered_set<std::string>* image_classes,
+                 bool boot_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,
@@ -435,6 +436,10 @@
   // according to the profile file.
   bool ShouldCompileBasedOnProfile(const MethodReference& method_ref) const;
 
+  // Checks whether profile guided verification is enabled and if the method should be verified
+  // according to the profile file.
+  bool ShouldVerifyClassBasedOnProfile(const DexFile& dex_file, uint16_t class_idx) const;
+
   void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
       REQUIRES(!compiled_classes_lock_);
 
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 3bf8921..71eb787 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -197,6 +197,8 @@
       compiler_filter_ = CompilerOptions::kEverything;
     } else if (strcmp(compiler_filter_string, "time") == 0) {
       compiler_filter_ = CompilerOptions::kTime;
+    } else if (strcmp(compiler_filter_string, "verify-profile") == 0) {
+      compiler_filter_ = CompilerOptions::kVerifyProfile;
     } else {
       Usage("Unknown --compiler-filter value %s", compiler_filter_string);
     }
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 4db82a6..f13c7af 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -39,6 +39,7 @@
     kSpeed,               // Maximize runtime performance.
     kEverything,          // Force compilation of everything capable of being compiled.
     kTime,                // Compile methods, but minimize compilation time.
+    kVerifyProfile,       // Verify only the classes in the profile.
   };
 
   // Guide heuristics to determine whether to compile method if profile data not available.
@@ -103,13 +104,14 @@
 
   bool IsCompilationEnabled() const {
     return compiler_filter_ != CompilerOptions::kVerifyNone &&
-        compiler_filter_ != CompilerOptions::kInterpretOnly &&
-        compiler_filter_ != CompilerOptions::kVerifyAtRuntime;
+           compiler_filter_ != CompilerOptions::kInterpretOnly &&
+           compiler_filter_ != CompilerOptions::kVerifyAtRuntime &&
+           compiler_filter_ != CompilerOptions::kVerifyProfile;
   }
 
   bool IsVerificationEnabled() const {
     return compiler_filter_ != CompilerOptions::kVerifyNone &&
-        compiler_filter_ != CompilerOptions::kVerifyAtRuntime;
+           compiler_filter_ != CompilerOptions::kVerifyAtRuntime;
   }
 
   bool NeverVerify() const {
@@ -120,6 +122,10 @@
     return compiler_filter_ == CompilerOptions::kVerifyAtRuntime;
   }
 
+  bool VerifyOnlyProfile() const {
+    return compiler_filter_ == CompilerOptions::kVerifyProfile;
+  }
+
   size_t GetHugeMethodThreshold() const {
     return huge_method_threshold_;
   }
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index d3b065c..976552a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -256,6 +256,7 @@
   UsageError("  --compiler-filter="
                 "(verify-none"
                 "|verify-at-runtime"
+                "|verify-profile"
                 "|interpret-only"
                 "|space"
                 "|balanced"
@@ -263,6 +264,7 @@
                 "|everything"
                 "|time):");
   UsageError("      select compiler filter.");
+  UsageError("      verify-profile requires a --profile(-fd) to also be passed in.");
   UsageError("      Example: --compiler-filter=everything");
   UsageError("      Default: speed");
   UsageError("");
@@ -808,10 +810,16 @@
       }
     }
 
-    if (!profile_file_.empty() && (profile_file_fd_ != kInvalidFd)) {
+    const bool have_profile_file = !profile_file_.empty();
+    const bool have_profile_fd = profile_file_fd_ != kInvalidFd;
+    if (have_profile_file && have_profile_fd) {
       Usage("Profile file should not be specified with both --profile-file-fd and --profile-file");
     }
 
+    if (compiler_options_->IsVerificationEnabled() && !have_profile_file && !have_profile_fd) {
+      Usage("verify-profile compiler filter must be used with a profile file or fd");
+    }
+
     if (!parser_options->oat_symbols.empty()) {
       oat_unstripped_ = std::move(parser_options->oat_symbols);
     }
@@ -1453,8 +1461,8 @@
     }
 
     /*
-     * If we're not in interpret-only or verify-none or verify-at-runtime mode, go ahead and
-     * compile small applications.  Don't bother to check if we're doing the image.
+     * If we're not in interpret-only or verify-none or verify-at-runtime or verify-profile mode,
+     * go ahead and compile small applications.  Don't bother to check if we're doing the image.
      */
     if (!IsBootImage() &&
         compiler_options_->IsCompilationEnabled() &&
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 6a2b702..53d952f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3976,7 +3976,12 @@
   oat_file_class_status = oat_dex_file->GetOatClass(class_def_index).GetStatus();
   if (oat_file_class_status == mirror::Class::kStatusVerified ||
       oat_file_class_status == mirror::Class::kStatusInitialized) {
-      return true;
+    return true;
+  }
+  // If we only verified a subset of the classes at compile time, we can end up with classes that
+  // were resolved by the verifier.
+  if (oat_file_class_status == mirror::Class::kStatusResolved) {
+    return false;
   }
   if (oat_file_class_status == mirror::Class::kStatusRetryVerificationAtRuntime) {
     // Compile time verification failed with a soft error. Compile time verification can fail
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index 2d4777b..e805ce1 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -374,6 +374,18 @@
   return false;
 }
 
+bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const {
+  auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation()));
+  if (info_it != info_.end()) {
+    if (dex_file.GetLocationChecksum() != info_it->second.checksum) {
+      return false;
+    }
+    const std::set<uint16_t>& classes = info_it->second.class_set;
+    return classes.find(class_def_idx) != classes.end();
+  }
+  return false;
+}
+
 uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
   uint32_t total = 0;
   for (const auto& it : info_) {
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index 52cab92..fb07f8c 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -61,6 +61,9 @@
   // Returns true if the method reference is present in the profiling info.
   bool ContainsMethod(const MethodReference& method_ref) const;
 
+  // Returns true if the class is present in the profiling info.
+  bool ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const;
+
   // Dumps all the loaded profile info into a string and returns it.
   // If dex_files is not null then the method indices will be resolved to their
   // names.