Jit-zygote: Postpone pre-compilation until boot is completed.

Compile only needed (hot) methods during boot.

This saves about 1.5s from jit-zygote boot time.

Test: device boots
Bug: 119800099
Change-Id: If98540e42634bf1e9701231e5174d724e897ce67
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 3eeb0fa..b596ea7 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -169,6 +169,7 @@
 Jit::Jit(JitCodeCache* code_cache, JitOptions* options)
     : code_cache_(code_cache),
       options_(options),
+      boot_completed_lock_("Jit::boot_completed_lock_"),
       cumulative_timings_("JIT timings"),
       memory_use_("Memory used for compilation", 16),
       lock_("JIT memory use lock") {}
@@ -702,7 +703,7 @@
         dex_files_,
         profile,
         loader,
-        /* add_to_queue= */ false);
+        /* add_to_queue= */ true);
   }
 
   void Finalize() override {
@@ -756,7 +757,8 @@
                                    uint32_t method_idx,
                                    Handle<mirror::DexCache> dex_cache,
                                    Handle<mirror::ClassLoader> class_loader,
-                                   bool add_to_queue) {
+                                   bool add_to_queue,
+                                   bool compile_after_boot) {
   ArtMethod* method = class_linker->ResolveMethodWithoutInvokeType(
       method_idx, dex_cache, class_loader);
   if (method == nullptr) {
@@ -778,8 +780,16 @@
             "Lcom/android/internal/os/ZygoteServer;")) {
       CompileMethod(method, self, /* baseline= */ false, /* osr= */ false, /* prejit= */ true);
     } else {
-      thread_pool_->AddTask(self,
-          new JitCompileTask(method, JitCompileTask::TaskKind::kPreCompile));
+      Task* task = new JitCompileTask(method, JitCompileTask::TaskKind::kPreCompile);
+      if (compile_after_boot) {
+        MutexLock mu(Thread::Current(), boot_completed_lock_);
+        if (!boot_completed_) {
+          tasks_after_boot_.push_back(task);
+          return true;
+        }
+        DCHECK(tasks_after_boot_.empty());
+      }
+      thread_pool_->AddTask(self, task);
       return true;
     }
   }
@@ -820,7 +830,8 @@
                                  pair.second,
                                  dex_caches[pair.first],
                                  class_loader,
-                                 add_to_queue)) {
+                                 add_to_queue,
+                                 /*compile_after_boot=*/false)) {
       ++added_to_queue;
     }
   }
@@ -893,7 +904,8 @@
                                    method_idx,
                                    dex_cache,
                                    class_loader,
-                                   add_to_queue)) {
+                                   add_to_queue,
+                                   /*compile_after_boot=*/true)) {
         ++added_to_queue;
       }
     }
@@ -1141,6 +1153,19 @@
   thread_pool_->CreateThreads();
 }
 
+void Jit::BootCompleted() {
+  Thread* self = Thread::Current();
+  std::deque<Task*> tasks;
+  {
+    MutexLock mu(self, boot_completed_lock_);
+    tasks = std::move(tasks_after_boot_);
+    boot_completed_ = true;
+  }
+  for (Task* task : tasks) {
+    thread_pool_->AddTask(self, task);
+  }
+}
+
 bool Jit::CanEncodeMethod(ArtMethod* method, bool is_for_shared_region) const {
   return !is_for_shared_region ||
       Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(method->GetDeclaringClass());
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 1b57754..c264f7c 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -329,6 +329,9 @@
   // Adjust state after forking.
   void PostZygoteFork();
 
+  // Called when system finishes booting.
+  void BootCompleted();
+
   // Compile methods from the given profile (.prof extension). If `add_to_queue`
   // is true, methods in the profile are added to the JIT queue. Otherwise they are compiled
   // directly.
@@ -375,7 +378,8 @@
                                 uint32_t method_idx,
                                 Handle<mirror::DexCache> dex_cache,
                                 Handle<mirror::ClassLoader> class_loader,
-                                bool add_to_queue)
+                                bool add_to_queue,
+                                bool compile_after_boot)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Compile the method if the number of samples passes a threshold.
@@ -402,6 +406,10 @@
   std::unique_ptr<ThreadPool> thread_pool_;
   std::vector<std::unique_ptr<OatDexFile>> type_lookup_tables_;
 
+  Mutex boot_completed_lock_;
+  bool boot_completed_ GUARDED_BY(boot_completed_lock_) = false;
+  std::deque<Task*> tasks_after_boot_ GUARDED_BY(boot_completed_lock_);
+
   // Performance monitoring.
   CumulativeLogger cumulative_timings_;
   Histogram<uint64_t> memory_use_ GUARDED_BY(lock_);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 410c229..ca41ae8 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -46,6 +46,7 @@
 #include "gc/space/image_space.h"
 #include "gc/task_processor.h"
 #include "intern_table.h"
+#include "jit/jit.h"
 #include "jni/java_vm_ext.h"
 #include "jni/jni_internal.h"
 #include "mirror/array-alloc-inl.h"
@@ -724,6 +725,14 @@
   return Runtime::Current()->GetHeap()->HasBootImageSpace() ? JNI_TRUE : JNI_FALSE;
 }
 
+static void VMRuntime_bootCompleted(JNIEnv* env ATTRIBUTE_UNUSED,
+                                    jclass klass ATTRIBUTE_UNUSED) {
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (jit != nullptr) {
+    jit->BootCompleted();
+  }
+}
+
 static JNINativeMethod gMethods[] = {
   FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
   NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
@@ -773,6 +782,7 @@
   NATIVE_METHOD(VMRuntime, setDedupeHiddenApiWarnings, "(Z)V"),
   NATIVE_METHOD(VMRuntime, setProcessPackageName, "(Ljava/lang/String;)V"),
   NATIVE_METHOD(VMRuntime, setProcessDataDirectory, "(Ljava/lang/String;)V"),
+  NATIVE_METHOD(VMRuntime, bootCompleted, "()V"),
 };
 
 void register_dalvik_system_VMRuntime(JNIEnv* env) {