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
(cherry picked from commit a079e3aa62cceb76c1c1811e6e09bcaf75e20289)
Change-Id: Iad5aa1adee3aa6c2408820e8cbbab2d4412021b8
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index cd9d80f..ea16cb2 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -383,6 +383,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";
}
@@ -830,10 +833,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);
}
@@ -918,6 +923,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(
@@ -2197,6 +2218,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();
@@ -2665,6 +2690,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 4ef26dd..64a06a2 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 43bdbf3..b4389d3 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -195,6 +195,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 fbfa7c8..59698af 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.
@@ -102,13 +103,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 {
@@ -119,6 +121,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 2981011..c2d5e4d 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("");
@@ -805,10 +807,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);
}
@@ -1450,8 +1458,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 d8e309d..e2ef7ac 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3964,7 +3964,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 ecf34f5..f181ca3 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -367,6 +367,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 ee7ce27..df03244 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -60,6 +60,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.