In GetDexoptNeeded, check whether the oat file was compiled without image.

If there now is an image on disk, return that we need to recompile.

Test: manual
Bug: 214376933
Change-Id: If229a892e2729ffa882cb56a0b983df1bd94acc5
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index c9e6bbf..5e01aaa 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -1156,20 +1156,9 @@
   LOG(INFO) << "Successfully mapped boot image methods";
 }
 
-// Return whether a boot image has a profile. This means we'll need to pre-JIT
-// methods in that profile for performance.
-static bool HasImageWithProfile() {
-  for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
-    if (!space->GetProfileFiles().empty()) {
-      return true;
-    }
-  }
-  return false;
-}
-
 bool Jit::InZygoteUsingJit() {
   Runtime* runtime = Runtime::Current();
-  return runtime->IsZygote() && HasImageWithProfile() && runtime->UseJitCompilation();
+  return runtime->IsZygote() && runtime->HasImageWithProfile() && runtime->UseJitCompilation();
 }
 
 void Jit::CreateThreadPool() {
@@ -1290,7 +1279,7 @@
   if (runtime->IsSystemServer() &&
       UseJitCompilation() &&
       options_->UseProfiledJitCompilation() &&
-      HasImageWithProfile() &&
+      runtime->HasImageWithProfile() &&
       !runtime->IsJavaDebuggable()) {
     thread_pool_->AddTask(Thread::Current(), new JitProfileTask(dex_files, class_loader));
   }
@@ -1624,11 +1613,12 @@
   // JitAtFirstUse compiles the methods synchronously on mutator threads. While this should work
   // in theory it is causing deadlocks in some jvmti tests related to Jit GC. Hence, disabling
   // Jit GC for now (b/147208992).
-  code_cache_->SetGarbageCollectCode(!jit_compiler_->GenerateDebugInfo() &&
-      !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() &&
+  code_cache_->SetGarbageCollectCode(
+      !jit_compiler_->GenerateDebugInfo() &&
+      !runtime->GetInstrumentation()->AreExitStubsInstalled() &&
       !JitAtFirstUse());
 
-  if (is_system_server && HasImageWithProfile()) {
+  if (is_system_server && runtime->HasImageWithProfile()) {
     // Disable garbage collection: we don't want it to delete methods we're compiling
     // through boot and system server profiles.
     // TODO(ngeoffray): Fix this so we still collect deoptimized and unused code.
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 7d2196f..914d2dd 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -946,8 +946,28 @@
     VLOG(oat) << "Compiler filter not okay because Profile changed";
     return false;
   }
-  return downgrade ? !CompilerFilter::IsBetter(current, target) :
-    CompilerFilter::IsAsGoodAs(current, target);
+
+  if (downgrade) {
+    return !CompilerFilter::IsBetter(current, target);
+  }
+
+  if (CompilerFilter::DependsOnImageChecksum(current) &&
+      CompilerFilter::IsAsGoodAs(current, target)) {
+    // If the oat file has been compiled without an image, and the runtime is
+    // now running with an image loaded from disk, return that we need to
+    // re-compile. The recompilation will generate a better oat file, and with an app
+    // image for profile guided compilation.
+    const char* oat_boot_class_path_checksums =
+        file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
+    if (oat_boot_class_path_checksums != nullptr &&
+        !StartsWith(oat_boot_class_path_checksums, "i") &&
+        !Runtime::Current()->HasImageWithProfile()) {
+      DCHECK(!file->GetOatHeader().RequiresImage());
+      return false;
+    }
+  }
+
+  return CompilerFilter::IsAsGoodAs(current, target);
 }
 
 bool OatFileAssistant::ClassLoaderContextIsOkay(const OatFile& oat_file) const {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index b4d6fd4..b090022 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -3397,4 +3397,13 @@
   }
 }
 
+bool Runtime::HasImageWithProfile() const {
+  for (gc::space::ImageSpace* space : GetHeap()->GetBootImageSpaces()) {
+    if (!space->GetProfileFiles().empty()) {
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 9f835bb..e7b71e2 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -1069,6 +1069,10 @@
     return apex_versions_;
   }
 
+  // Return whether a boot image has a profile. This means it's an in-memory
+  // image rather that an image loaded from disk.
+  bool HasImageWithProfile() const;
+
   // Trigger a flag reload from system properties or device congfigs.
   //
   // Should only be called from runtime init and zygote post fork as