Optimize OatWriter when we don't compile any method.

- Don't write any quickening info.
- Don't visit methods.

Saves ~20% of compilation times.

Test: test-art-host

Change-Id: Ib18fd06c0ca42308e1d81401de0ee3e6297de0ce
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index ec1642e..dfd04e3 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -423,7 +423,7 @@
   // Compile:
   // 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
   //    compilation.
-  if (!GetCompilerOptions().VerifyAtRuntime() && !GetCompilerOptions().VerifyOnlyProfile()) {
+  if (GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
     Compile(class_loader, dex_files, timings);
   }
   if (dump_stats_) {
@@ -494,11 +494,15 @@
     // TODO: we unquicken unconditionnally, as we don't know
     // if the boot image has changed. How exactly we'll know is under
     // experimentation.
-    TimingLogger::ScopedTiming t("Unquicken", timings);
-    // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening
-    // optimization does not depend on the boot image (the optimization relies on not
-    // having final fields in a class, which does not change for an app).
-    Unquicken(dex_files, vdex_file->GetQuickeningInfo(), /* decompile_return_instruction */ false);
+    if (vdex_file->GetQuickeningInfo().size() != 0) {
+      TimingLogger::ScopedTiming t("Unquicken", timings);
+      // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening
+      // optimization does not depend on the boot image (the optimization relies on not
+      // having final fields in a class, which does not change for an app).
+      Unquicken(dex_files,
+                vdex_file->GetQuickeningInfo(),
+                /* decompile_return_instruction */ false);
+    }
     Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
         new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData()));
   }
@@ -510,10 +514,7 @@
     const DexFile& dex_file, const DexFile::ClassDef& class_def)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   auto* const runtime = Runtime::Current();
-  if (runtime->UseJitCompilation() || driver.GetCompilerOptions().VerifyAtRuntime()) {
-    // Verify at runtime shouldn't dex to dex since we didn't resolve of verify.
-    return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile;
-  }
+  DCHECK(driver.GetCompilerOptions().IsAnyMethodCompilationEnabled());
   const char* descriptor = dex_file.GetClassDescriptor(class_def);
   ClassLinker* class_linker = runtime->GetClassLinker();
   mirror::Class* klass = class_linker->FindClass(self, descriptor, class_loader);
@@ -954,24 +955,17 @@
   LoadImageClasses(timings);
   VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
 
-  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.
-  // Let the verifier resolve as needed for the verify_only_profile case.
-  if ((never_verify || verification_enabled) && !verify_only_profile) {
+  if (compiler_options_->IsAnyMethodCompilationEnabled()) {
     Resolve(class_loader, dex_files, timings);
     VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
   }
 
-  if (never_verify) {
+  if (compiler_options_->AssumeClassesAreVerified()) {
     VLOG(compiler) << "Verify none mode specified, skipping verification.";
     SetVerified(class_loader, dex_files, timings);
   }
 
-  if (!verification_enabled) {
+  if (!compiler_options_->IsVerificationEnabled()) {
     return;
   }
 
@@ -989,7 +983,7 @@
                << "situations. Please check the log.";
   }
 
-  if (!verify_only_profile) {
+  if (compiler_options_->IsAnyMethodCompilationEnabled()) {
     InitializeClasses(class_loader, dex_files, timings);
     VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
   }
@@ -2069,7 +2063,7 @@
         for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
           const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
           if (set.find(class_def.class_idx_) == set.end()) {
-            if (GetCompilerOptions().VerifyOnlyProfile()) {
+            if (!GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
               // Just update the compiled_classes_ map. The compiler doesn't need to resolve
               // the type.
               compiled_classes_.Overwrite(
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 9c62f80..6894cd5 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -109,7 +109,7 @@
     return CompilerFilter::IsVerificationEnabled(compiler_filter_);
   }
 
-  bool NeverVerify() const {
+  bool AssumeClassesAreVerified() const {
     return compiler_filter_ == CompilerFilter::kVerifyNone;
   }
 
@@ -117,6 +117,10 @@
     return compiler_filter_ == CompilerFilter::kVerifyProfile;
   }
 
+  bool IsAnyMethodCompilationEnabled() const {
+    return CompilerFilter::IsAnyMethodCompilationEnabled(compiler_filter_);
+  }
+
   size_t GetHugeMethodThreshold() const {
     return huge_method_threshold_;
   }
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index bebd5f5..2d4bbce 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1468,30 +1468,32 @@
       if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) {
         return false;
       }
-      const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
-      const uint8_t* class_data = dex_file->GetClassData(class_def);
-      if (class_data != nullptr) {  // ie not an empty class, such as a marker interface
-        ClassDataItemIterator it(*dex_file, class_data);
-        while (it.HasNextStaticField()) {
-          it.Next();
-        }
-        while (it.HasNextInstanceField()) {
-          it.Next();
-        }
-        size_t class_def_method_index = 0u;
-        while (it.HasNextDirectMethod()) {
-          if (!visitor->VisitMethod(class_def_method_index, it)) {
-            return false;
+      if (compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+        const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+        const uint8_t* class_data = dex_file->GetClassData(class_def);
+        if (class_data != nullptr) {  // ie not an empty class, such as a marker interface
+          ClassDataItemIterator it(*dex_file, class_data);
+          while (it.HasNextStaticField()) {
+            it.Next();
           }
-          ++class_def_method_index;
-          it.Next();
-        }
-        while (it.HasNextVirtualMethod()) {
-          if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) {
-            return false;
+          while (it.HasNextInstanceField()) {
+            it.Next();
           }
-          ++class_def_method_index;
-          it.Next();
+          size_t class_def_method_index = 0u;
+          while (it.HasNextDirectMethod()) {
+            if (!visitor->VisitMethod(class_def_method_index, it)) {
+              return false;
+            }
+            ++class_def_method_index;
+            it.Next();
+          }
+          while (it.HasNextVirtualMethod()) {
+            if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) {
+              return false;
+            }
+            ++class_def_method_index;
+            it.Next();
+          }
         }
       }
       if (UNLIKELY(!visitor->EndClass())) {
@@ -1548,6 +1550,9 @@
 }
 
 size_t OatWriter::InitOatMaps(size_t offset) {
+  if (!compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+    return offset;
+  }
   InitMapMethodVisitor visitor(this, offset);
   bool success = VisitDexMethods(&visitor);
   DCHECK(success);
@@ -1594,6 +1599,9 @@
 }
 
 size_t OatWriter::InitOatCodeDexFiles(size_t offset) {
+  if (!compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+    return offset;
+  }
   InitCodeMethodVisitor code_visitor(this, offset, vdex_quickening_info_offset_);
   bool success = VisitDexMethods(&code_visitor);
   DCHECK(success);
@@ -1745,19 +1753,24 @@
     return false;
   }
 
-  WriteQuickeningInfoMethodVisitor visitor(this, vdex_out, start_offset);
-  if (!VisitDexMethods(&visitor)) {
-    PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
-    return false;
+  if (compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+    WriteQuickeningInfoMethodVisitor visitor(this, vdex_out, start_offset);
+    if (!VisitDexMethods(&visitor)) {
+      PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
+      return false;
+    }
+
+    if (!vdex_out->Flush()) {
+      PLOG(ERROR) << "Failed to flush stream after writing quickening info."
+                  << " File: " << vdex_out->GetLocation();
+      return false;
+    }
+    size_quickening_info_ = visitor.GetNumberOfWrittenBytes();
+  } else {
+    // We know we did not quicken.
+    size_quickening_info_ = 0;
   }
 
-  if (!vdex_out->Flush()) {
-    PLOG(ERROR) << "Failed to flush stream after writing quickening info."
-                << " File: " << vdex_out->GetLocation();
-    return false;
-  }
-
-  size_quickening_info_ = visitor.GetNumberOfWrittenBytes();
   vdex_size_ += size_quickening_info_;
   return true;
 }
diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc
index 6e3e1d8..dc89d32 100644
--- a/runtime/compiler_filter.cc
+++ b/runtime/compiler_filter.cc
@@ -60,6 +60,26 @@
   UNREACHABLE();
 }
 
+bool CompilerFilter::IsAnyMethodCompilationEnabled(Filter filter) {
+  switch (filter) {
+    case CompilerFilter::kVerifyNone:
+    case CompilerFilter::kVerifyAtRuntime:
+    case CompilerFilter::kVerifyProfile: return false;
+
+    case CompilerFilter::kInterpretOnly:
+    case CompilerFilter::kSpaceProfile:
+    case CompilerFilter::kSpace:
+    case CompilerFilter::kBalanced:
+    case CompilerFilter::kTime:
+    case CompilerFilter::kSpeedProfile:
+    case CompilerFilter::kSpeed:
+    case CompilerFilter::kLayoutProfile:
+    case CompilerFilter::kEverythingProfile:
+    case CompilerFilter::kEverything: return true;
+  }
+  UNREACHABLE();
+}
+
 bool CompilerFilter::IsVerificationEnabled(Filter filter) {
   switch (filter) {
     case CompilerFilter::kVerifyNone:
diff --git a/runtime/compiler_filter.h b/runtime/compiler_filter.h
index 781d43a..7eb5f9a 100644
--- a/runtime/compiler_filter.h
+++ b/runtime/compiler_filter.h
@@ -52,6 +52,11 @@
   static bool IsBytecodeCompilationEnabled(Filter filter);
 
   // Returns true if an oat file with this compiler filter contains
+  // compiled executable code for bytecode, JNI methods, or quickened dex
+  // bytecode.
+  static bool IsAnyMethodCompilationEnabled(Filter filter);
+
+  // Returns true if an oat file with this compiler filter contains
   // compiled executable code for JNI methods.
   static bool IsJniCompilationEnabled(Filter filter);
 
diff --git a/test/628-vdex/run b/test/628-vdex/run
index f1b0a95..4cbcea3 100644
--- a/test/628-vdex/run
+++ b/test/628-vdex/run
@@ -14,4 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-exec ${RUN} --vdex "${@}"
+exec ${RUN} -Xcompiler-option --compiler-filter=verify-profile --vdex "${@}"