Delay entrypoint update until visibly initialized.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing --jit
Test: aosp_taimen-userdebug boots.
Test: run-gtests.sh
Test: testrunner.py --target --optimizing --jit
Bug: 18161648
Change-Id: Idde0cbc848f19236319426bc82ac10b8b8bb9ee9
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 3f87154..2e0f1eb 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -432,12 +432,12 @@
 
 template <ReadBarrierOption kReadBarrierOption>
 bool ArtMethod::NeedsInitializationCheck() {
-  // Knowing if the class is visibly initialized is only for JIT/AOT compiled
-  // code to avoid the memory barrier. For callers of `NeedsInitializationCheck`
-  // it's enough to just check whether the class is initialized.
+  // The class needs to be visibly initialized before we can use entrypoints to
+  // compiled code for static methods. See b/18161648 . The class initializer is
+  // special as it is invoked during initialization and does not need the check.
   return IsStatic() &&
       !IsConstructor() &&
-      !GetDeclaringClass<kReadBarrierOption>()->IsInitialized();
+      !GetDeclaringClass<kReadBarrierOption>()->IsVisiblyInitialized();
 }
 
 }  // namespace art
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index de4db67..650f907 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -303,6 +303,7 @@
           vm->DeleteWeakGlobalRef(self, classes_[i]);
           if (klass != nullptr) {
             mirror::Class::SetStatus(klass, ClassStatus::kVisiblyInitialized, self);
+            class_linker_->FixupStaticTrampolines(klass.Get());
           }
         }
         num_classes_ = 0u;
@@ -402,12 +403,14 @@
     // Thanks to the x86 memory model, we do not need any memory fences and
     // we can immediately mark the class as visibly initialized.
     mirror::Class::SetStatus(klass, ClassStatus::kVisiblyInitialized, self);
+    FixupStaticTrampolines(klass.Get());
     return nullptr;
   }
   if (Runtime::Current()->IsActiveTransaction()) {
     // Transactions are single-threaded, so we can mark the class as visibly intialized.
     // (Otherwise we'd need to track the callback's entry in the transaction for rollback.)
     mirror::Class::SetStatus(klass, ClassStatus::kVisiblyInitialized, self);
+    FixupStaticTrampolines(klass.Get());
     return nullptr;
   }
   mirror::Class::SetStatus(klass, ClassStatus::kInitialized, self);
@@ -3697,10 +3700,13 @@
 
 void ClassLinker::FixupStaticTrampolines(ObjPtr<mirror::Class> klass) {
   ScopedAssertNoThreadSuspension sants(__FUNCTION__);
-  DCHECK(klass->IsInitialized()) << klass->PrettyDescriptor();
+  DCHECK(klass->IsVisiblyInitialized()) << klass->PrettyDescriptor();
   if (klass->NumDirectMethods() == 0) {
     return;  // No direct methods => no static methods.
   }
+  if (UNLIKELY(klass->IsProxyClass())) {
+    return;
+  }
   Runtime* runtime = Runtime::Current();
   if (!runtime->IsStarted()) {
     if (runtime->IsAotCompiler() || runtime->GetHeap()->HasBootImageSpace()) {
@@ -3735,7 +3741,7 @@
       quick_code = oat_method.GetQuickCode();
     }
     // Check if we have JIT compiled code for it.
-    jit::Jit* jit = Runtime::Current()->GetJit();
+    jit::Jit* jit = runtime->GetJit();
     if (quick_code == nullptr && jit != nullptr) {
       quick_code = jit->GetCodeCache()->GetSavedEntryPointOfPreCompiledMethod(method);
     }
@@ -5671,8 +5677,6 @@
         LOG(INFO) << "Initialized class " << klass->GetDescriptor(&temp) << " from " <<
             klass->GetLocation();
       }
-      // Opportunistically set static method trampolines to their destination.
-      FixupStaticTrampolines(klass.Get());
     }
   }
   if (callback != nullptr) {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index ff23385..e1db1f6 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -119,6 +119,21 @@
     code_ = code;
   }
 
+  void UpdateEntryPoints(const void* entrypoint) REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(IsCompiled());
+    DCHECK(entrypoint == OatQuickMethodHeader::FromCodePointer(GetCode())->GetEntryPoint());
+    instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation();
+    for (ArtMethod* m : GetMethods()) {
+      // Because `m` might be in the process of being deleted:
+      // - Call the dedicated method instead of the more generic UpdateMethodsCode
+      // - Check the class status without a read barrier.
+      // TODO: Use ReadBarrier::IsMarked() if not null to check the class status.
+      if (!m->NeedsInitializationCheck<kWithoutReadBarrier>()) {
+        instrum->UpdateNativeMethodsCodeToJitCode(m, entrypoint);
+      }
+    }
+  }
+
   const void* GetCode() const {
     return code_;
   }
@@ -705,15 +720,7 @@
       DCHECK(ContainsElement(data->GetMethods(), method))
           << "Entry inserted in NotifyCompilationOf() should contain this method.";
       data->SetCode(code_ptr);
-      instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation();
-      for (ArtMethod* m : data->GetMethods()) {
-        // Because `m` might be in the process of being deleted:
-        // - Call the dedicated method instead of the more generic UpdateMethodsCode
-        // - Check the class status without a read barrier.
-        if (!m->NeedsInitializationCheck<kWithoutReadBarrier>()) {
-          instrum->UpdateNativeMethodsCodeToJitCode(m, method_header->GetEntryPoint());
-        }
-      }
+      data->UpdateEntryPoints(method_header->GetEntryPoint());
     } else {
       if (method->IsPreCompiled() && IsSharedRegion(*region)) {
         zygote_map_.Put(code_ptr, method);
@@ -1555,6 +1562,12 @@
   if (method->NeedsInitializationCheck() && !prejit) {
     // Unless we're pre-jitting, we currently don't save the JIT compiled code if we cannot
     // update the entrypoint due to needing an initialization check.
+    if (method->GetDeclaringClass()->IsInitialized()) {
+      // Request visible initialization but do not block to allow compiling other methods.
+      // Hopefully, this will complete by the time the method becomes hot again.
+      Runtime::Current()->GetClassLinker()->MakeInitializedClassesVisiblyInitialized(
+          self, /*wait=*/ false);
+    }
     VLOG(jit) << "Not compiling "
               << method->PrettyMethod()
               << " because it has the resolution stub";
@@ -1587,15 +1600,7 @@
       // changed these entrypoints to GenericJNI in preparation for a full GC, we may
       // as well change them back as this stub shall not be collected anyway and this
       // can avoid a few expensive GenericJNI calls.
-      instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
-      for (ArtMethod* m : data->GetMethods()) {
-        // Because `m` might be in the process of being deleted:
-        // - Call the dedicated method instead of the more generic UpdateMethodsCode
-        // - Check the class status without a read barrier.
-        if (!m->NeedsInitializationCheck<kWithoutReadBarrier>()) {
-          instrumentation->UpdateNativeMethodsCodeToJitCode(m, entrypoint);
-        }
-      }
+      data->UpdateEntryPoints(entrypoint);
       if (collection_in_progress_) {
         if (!IsInZygoteExecSpace(data->GetCode())) {
           GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(data->GetCode()));
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 2c1a43a..1cf8ee5 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -221,14 +221,28 @@
       ThrowIllegalStateException(msg.c_str());
       return;
     }
-    // We force initialization of the declaring class to make sure the method doesn't keep
-    // the resolution stub as entrypoint.
+    // We force visible initialization of the declaring class to make sure the method
+    // doesn't keep the resolution stub as entrypoint.
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> h_klass(hs.NewHandle(method->GetDeclaringClass()));
-    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_klass, true, true)) {
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    if (!class_linker->EnsureInitialized(self, h_klass, true, true)) {
       self->AssertPendingException();
       return;
     }
+    if (UNLIKELY(!h_klass->IsInitialized())) {
+      // Must be initializing in this thread.
+      CHECK_EQ(h_klass->GetStatus(), ClassStatus::kInitializing);
+      CHECK_EQ(h_klass->GetClinitThreadId(), self->GetTid());
+      std::string msg(method->PrettyMethod());
+      msg += ": is not safe to jit because the class is being initialized in this thread!";
+      ThrowIllegalStateException(msg.c_str());
+      return;
+    }
+    if (!h_klass->IsVisiblyInitialized()) {
+      ScopedThreadSuspension sts(self, ThreadState::kNative);
+      class_linker->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
+    }
   }
   jit::Jit* jit = GetJitIfEnabled();
   jit::JitCodeCache* code_cache = jit->GetCodeCache();