Revert "More entrypoint handling cleanup."

This reverts commit 67e48ecc200bee6e4ac784c97446bb949fb20a9e.

Reason for revert: Test failure on target.

Change-Id: I94c96db18f79d7e214435b3e475586d8c5d0ab58
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 82b1cc2..e227aa2 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3309,6 +3309,66 @@
                                          image_pointer_size_);
 }
 
+bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) {
+  ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+  if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) {
+    return false;
+  }
+
+  if (quick_code == nullptr) {
+    return true;
+  }
+
+  Runtime* runtime = Runtime::Current();
+  instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
+  if (instr->InterpretOnly()) {
+    return true;
+  }
+
+  if (runtime->GetClassLinker()->IsQuickToInterpreterBridge(quick_code)) {
+    // Doing this check avoids doing compiled/interpreter transitions.
+    return true;
+  }
+
+  if (Thread::Current()->IsForceInterpreter()) {
+    // Force the use of interpreter when it is required by the debugger.
+    return true;
+  }
+
+  if (Thread::Current()->IsAsyncExceptionPending()) {
+    // Force use of interpreter to handle async-exceptions
+    return true;
+  }
+
+  if (quick_code == GetQuickInstrumentationEntryPoint()) {
+    const void* instr_target = instr->GetCodeForInvoke(method);
+    DCHECK_NE(instr_target, GetQuickInstrumentationEntryPoint()) << method->PrettyMethod();
+    return ShouldUseInterpreterEntrypoint(method, instr_target);
+  }
+
+  if (runtime->IsJavaDebuggable()) {
+    // For simplicity, we ignore precompiled code and go to the interpreter
+    // assuming we don't already have jitted code.
+    // We could look at the oat file where `quick_code` is being defined,
+    // and check whether it's been compiled debuggable, but we decided to
+    // only rely on the JIT for debuggable apps.
+    jit::Jit* jit = Runtime::Current()->GetJit();
+    return (jit == nullptr) || !jit->GetCodeCache()->ContainsPc(quick_code);
+  }
+
+  if (runtime->IsNativeDebuggable()) {
+    DCHECK(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse());
+    // If we are doing native debugging, ignore application's AOT code,
+    // since we want to JIT it (at first use) with extra stackmaps for native
+    // debugging. We keep however all AOT code from the boot image,
+    // since the JIT-at-first-use is blocking and would result in non-negligible
+    // startup performance impact.
+    return !runtime->GetHeap()->IsInBootImageOatFile(quick_code);
+  }
+
+  return false;
+}
+
 void ClassLinker::FixupStaticTrampolines(Thread* self, ObjPtr<mirror::Class> klass) {
   ScopedAssertNoThreadSuspension sants(__FUNCTION__);
   DCHECK(klass->IsVisiblyInitialized()) << klass->PrettyDescriptor();
@@ -3335,12 +3395,17 @@
     }
   }
   Runtime* runtime = Runtime::Current();
-  if (runtime->IsAotCompiler()) {
-    // We should not update entrypoints when running the transactional
-    // interpreter.
-    return;
+  if (!runtime->IsStarted()) {
+    if (runtime->IsAotCompiler() || runtime->GetHeap()->HasBootImageSpace()) {
+      return;  // OAT file unavailable.
+    }
   }
 
+  const DexFile& dex_file = klass->GetDexFile();
+  bool has_oat_class;
+  OatFile::OatClass oat_class = OatFile::FindOatClass(dex_file,
+                                                      klass->GetDexClassDefIndex(),
+                                                      &has_oat_class);
   // Link the code of methods skipped by LinkCode.
   for (size_t method_index = 0; method_index < num_direct_methods; ++method_index) {
     ArtMethod* method = klass->GetDirectMethod(method_index, pointer_size);
@@ -3348,8 +3413,42 @@
       // Only update static methods.
       continue;
     }
-    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
-    instrumentation->UpdateMethodsCode(method, instrumentation->GetCodeForInvoke(method));
+    const void* quick_code = nullptr;
+
+    // In order:
+    // 1) Check if we have AOT Code.
+    // 2) Check if we have JIT Code.
+    // 3) Check if we can use Nterp.
+    if (has_oat_class) {
+      OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
+      quick_code = oat_method.GetQuickCode();
+    }
+
+    jit::Jit* jit = runtime->GetJit();
+    if (quick_code == nullptr && jit != nullptr) {
+      quick_code = jit->GetCodeCache()->GetSavedEntryPointOfPreCompiledMethod(method);
+    }
+
+    if (quick_code == nullptr &&
+        interpreter::CanRuntimeUseNterp() &&
+        CanMethodUseNterp(method)) {
+      quick_code = interpreter::GetNterpEntryPoint();
+    }
+
+    // Check whether the method is native, in which case it's generic JNI.
+    if (quick_code == nullptr && method->IsNative()) {
+      quick_code = GetQuickGenericJniStub();
+    } else if (ShouldUseInterpreterEntrypoint(method, quick_code)) {
+      // Use interpreter entry point.
+      if (IsQuickToInterpreterBridge(method->GetEntryPointFromQuickCompiledCode())) {
+        // If we have the trampoline or the bridge already, no need to update.
+        // This saves in not dirtying boot image memory.
+        continue;
+      }
+      quick_code = GetQuickToInterpreterBridge();
+    }
+    CHECK(quick_code != nullptr);
+    runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code);
   }
   // Ignore virtual methods on the iterator.
 }
@@ -3378,7 +3477,6 @@
 
   // Method shouldn't have already been linked.
   DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
-  DCHECK(!method->GetDeclaringClass()->IsVisiblyInitialized());  // Actually ClassStatus::Idx.
 
   if (!method->IsInvokable()) {
     EnsureThrowsInvocationError(class_linker, method);
@@ -3392,13 +3490,46 @@
     const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);
     quick_code = oat_method.GetQuickCode();
   }
-  runtime->GetInstrumentation()->InitializeMethodsCode(method, quick_code);
+
+  bool enter_interpreter = class_linker->ShouldUseInterpreterEntrypoint(method, quick_code);
+
+  // Note: this mimics the logic in image_writer.cc that installs the resolution
+  // stub only if we have compiled code and the method needs a class initialization
+  // check.
+  if (quick_code == nullptr) {
+    if (method->IsNative()) {
+      method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
+    } else {
+      // Note we cannot use the nterp entrypoint because we do not know if the
+      // method will need the slow interpreter for lock verification. This will
+      // be updated in EnsureSkipAccessChecksMethods.
+      method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
+    }
+  } else if (enter_interpreter) {
+    method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
+  } else if (NeedsClinitCheckBeforeCall(method)) {
+    DCHECK(!method->GetDeclaringClass()->IsVisiblyInitialized());  // Actually ClassStatus::Idx.
+    // If we do have code but the method needs a class initialization check before calling
+    // that code, install the resolution stub that will perform the check.
+    // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
+    // after initializing class (see ClassLinker::InitializeClass method).
+    method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
+  } else {
+    method->SetEntryPointFromQuickCompiledCode(quick_code);
+  }
 
   if (method->IsNative()) {
     // Set up the dlsym lookup stub. Do not go through `UnregisterNative()`
     // as the extra processing for @CriticalNative is not needed yet.
     method->SetEntryPointFromJni(
         method->IsCriticalNative() ? GetJniDlsymLookupCriticalStub() : GetJniDlsymLookupStub());
+
+    if (enter_interpreter || quick_code == nullptr) {
+      // We have a native method here without code. Then it should have the generic JNI
+      // trampoline as entrypoint.
+      // TODO: this doesn't handle all the cases where trampolines may be installed.
+      DCHECK(class_linker->IsQuickGenericJniStub(method->GetEntryPointFromQuickCompiledCode()));
+    }
   }
 }
 
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index c40cea3..0d32271 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -718,6 +718,9 @@
       REQUIRES(!Locks::classlinker_classes_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  static bool ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                 ObjPtr<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index d3e631c..f1b52e0 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -211,7 +211,7 @@
          (code == GetQuickInstrumentationEntryPoint());
 }
 
-static void UpdateEntryPoints(ArtMethod* method, const void* quick_code)
+static void UpdateEntrypoints(ArtMethod* method, const void* quick_code)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (kIsDebugBuild) {
     if (NeedsClinitCheckBeforeCall(method) &&
@@ -226,11 +226,7 @@
       }
     }
   }
-  // If the method is from a boot image, don't dirty it if the entrypoint
-  // doesn't change.
-  if (method->GetEntryPointFromQuickCompiledCode() != quick_code) {
-    method->SetEntryPointFromQuickCompiledCode(quick_code);
-  }
+  method->SetEntryPointFromQuickCompiledCode(quick_code);
 }
 
 bool Instrumentation::CodeNeedsEntryExitStub(const void* code, ArtMethod* method) {
@@ -293,35 +289,6 @@
          Runtime::Current()->GetRuntimeCallbacks()->MethodNeedsDebugVersion(method);
 }
 
-static bool CanUseAotCode(ArtMethod* method, const void* quick_code)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (quick_code == nullptr) {
-    return false;
-  }
-  if (method->IsNative()) {
-    // AOT code for native methods can always be used.
-    return true;
-  }
-
-  Runtime* runtime = Runtime::Current();
-  // For simplicity, we never use AOT code for debuggable.
-  if (runtime->IsJavaDebuggable()) {
-    return false;
-  }
-
-  if (runtime->IsNativeDebuggable()) {
-    DCHECK(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse());
-    // If we are doing native debugging, ignore application's AOT code,
-    // since we want to JIT it (at first use) with extra stackmaps for native
-    // debugging. We keep however all AOT code from the boot image,
-    // since the JIT-at-first-use is blocking and would result in non-negligible
-    // startup performance impact.
-    return runtime->GetHeap()->IsInBootImageOatFile(quick_code);
-  }
-
-  return true;
-}
-
 static const void* GetOptimizedCodeFor(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(!Runtime::Current()->GetInstrumentation()->InterpretOnly(method));
   CHECK(method->IsInvokable()) << method->PrettyMethod();
@@ -330,10 +297,12 @@
   }
 
   // In debuggable mode, we can only use AOT code for native methods.
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  const void* aot_code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize());
-  if (CanUseAotCode(method, aot_code)) {
-    return aot_code;
+  if (!Runtime::Current()->IsJavaDebuggable() || method->IsNative()) {
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    const void* code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize());
+    if (code != nullptr) {
+      return code;
+    }
   }
 
   // If the method has been precompiled, there can be a JIT version.
@@ -345,67 +314,13 @@
     }
   }
 
-  // We need to check if the class has been verified for setting up nterp, as
-  // the verifier could punt the method to the switch interpreter in case we
-  // need to do lock counting.
-  if (interpreter::CanRuntimeUseNterp() &&
-      CanMethodUseNterp(method) &&
-      method->GetDeclaringClass()->IsVerified()) {
+  if (interpreter::CanRuntimeUseNterp() && CanMethodUseNterp(method)) {
     return interpreter::GetNterpEntryPoint();
   }
 
   return method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge();
 }
 
-void Instrumentation::InitializeMethodsCode(ArtMethod* method, const void* aot_code)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  // Use instrumentation entrypoints if instrumentation is installed.
-  if (UNLIKELY(EntryExitStubsInstalled())) {
-    if (!method->IsNative() && InterpretOnly()) {
-      UpdateEntryPoints(method, GetQuickToInterpreterBridge());
-    } else {
-      UpdateEntryPoints(method, GetQuickInstrumentationEntryPoint());
-    }
-    return;
-  }
-
-  if (UNLIKELY(IsForcedInterpretOnly())) {
-    UpdateEntryPoints(
-        method, method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge());
-    return;
-  }
-
-  // Special case if we need an initialization check.
-  if (NeedsClinitCheckBeforeCall(method)) {
-    // If we have code but the method needs a class initialization check before calling
-    // that code, install the resolution stub that will perform the check.
-    // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
-    // after initializing class (see ClassLinker::InitializeClass method).
-    // Note: this mimics the logic in image_writer.cc that installs the resolution
-    // stub only if we have compiled code and the method needs a class initialization
-    // check.
-    if (aot_code != nullptr || method->IsNative()) {
-      UpdateEntryPoints(method, GetQuickResolutionStub());
-    } else {
-      UpdateEntryPoints(method, GetQuickToInterpreterBridge());
-    }
-    return;
-  }
-
-  // Use the provided AOT code if possible.
-  if (CanUseAotCode(method, aot_code)) {
-    UpdateEntryPoints(method, aot_code);
-    return;
-  }
-
-  // Use default entrypoints.
-  // Note we cannot use the nterp entrypoint because we do not know if the
-  // method will need the slow interpreter for lock verification. This will
-  // be updated in ClassLinker::UpdateClassAfterVerification.
-  UpdateEntryPoints(
-      method, method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge());
-}
-
 void Instrumentation::InstallStubsForMethod(ArtMethod* method) {
   if (!method->IsInvokable() || method->IsProxyMethod()) {
     // Do not change stubs for these methods.
@@ -421,14 +336,14 @@
   // If the instrumentation needs to go through the interpreter, just update the
   // entrypoint to interpreter.
   if (InterpretOnly(method)) {
-    UpdateEntryPoints(method, GetQuickToInterpreterBridge());
+    UpdateEntrypoints(method, GetQuickToInterpreterBridge());
     return;
   }
 
   if (EntryExitStubsInstalled()) {
     // Install the instrumentation entry point if needed.
     if (CodeNeedsEntryExitStub(method->GetEntryPointFromQuickCompiledCode(), method)) {
-      UpdateEntryPoints(method, GetQuickInstrumentationEntryPoint());
+      UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint());
     }
     return;
   }
@@ -437,10 +352,10 @@
   CHECK_EQ(instrumentation_level_, InstrumentationLevel::kInstrumentNothing);
   // We need to have the resolution stub still if the class is not initialized.
   if (NeedsClinitCheckBeforeCall(method) && !method->GetDeclaringClass()->IsVisiblyInitialized()) {
-    UpdateEntryPoints(method, GetQuickResolutionStub());
+    UpdateEntrypoints(method, GetQuickResolutionStub());
     return;
   }
-  UpdateEntryPoints(method, GetOptimizedCodeFor(method));
+  UpdateEntrypoints(method, GetOptimizedCodeFor(method));
 }
 
 // Places the instrumentation exit pc as the return PC for every quick frame. This also allows
@@ -1028,7 +943,7 @@
   }
 }
 
-std::string Instrumentation::EntryPointString(const void* code) {
+static std::string EntrypointString(const void* code) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   jit::Jit* jit = Runtime::Current()->GetJit();
   if (class_linker->IsQuickToInterpreterBridge(code)) {
@@ -1051,41 +966,38 @@
   return "unknown";
 }
 
-void Instrumentation::UpdateMethodsCodeImpl(ArtMethod* method, const void* new_code) {
+void Instrumentation::UpdateMethodsCodeImpl(ArtMethod* method, const void* quick_code) {
   if (!EntryExitStubsInstalled()) {
     // Fast path: no instrumentation.
     DCHECK(!IsDeoptimized(method));
-    UpdateEntryPoints(method, new_code);
+    UpdateEntrypoints(method, quick_code);
     return;
   }
 
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  if (class_linker->IsQuickToInterpreterBridge(new_code)) {
+  if (class_linker->IsQuickToInterpreterBridge(quick_code)) {
     // It's always OK to update to the interpreter.
-    UpdateEntryPoints(method, new_code);
+    UpdateEntrypoints(method, quick_code);
     return;
   }
 
   if (IsDeoptimized(method)) {
     DCHECK(class_linker->IsQuickToInterpreterBridge(method->GetEntryPointFromQuickCompiledCode()))
-        << EntryPointString(method->GetEntryPointFromQuickCompiledCode());
+        << EntrypointString(method->GetEntryPointFromQuickCompiledCode());
     // Don't update, stay deoptimized.
     return;
   }
 
-  if (CodeNeedsEntryExitStub(new_code, method)) {
-    DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickInstrumentationEntryPoint() ||
-        class_linker->IsQuickToInterpreterBridge(method->GetEntryPointFromQuickCompiledCode()))
-              << EntryPointString(method->GetEntryPointFromQuickCompiledCode());
+  if (CodeNeedsEntryExitStub(quick_code, method)) {
     // If the code we want to update the method with still needs entry/exit stub, just skip.
     return;
   }
 
   // At this point, we can update as asked.
-  UpdateEntryPoints(method, new_code);
+  UpdateEntrypoints(method, quick_code);
 }
 
-void Instrumentation::UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* new_code) {
+void Instrumentation::UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* quick_code) {
   // We don't do any read barrier on `method`'s declaring class in this code, as the JIT might
   // enter here on a soon-to-be deleted ArtMethod. Updating the entrypoint is OK though, as
   // the ArtMethod is still in memory.
@@ -1093,12 +1005,12 @@
     // If stubs are installed don't update.
     return;
   }
-  UpdateEntryPoints(method, new_code);
+  UpdateEntrypoints(method, quick_code);
 }
 
-void Instrumentation::UpdateMethodsCode(ArtMethod* method, const void* new_code) {
+void Instrumentation::UpdateMethodsCode(ArtMethod* method, const void* quick_code) {
   DCHECK(method->GetDeclaringClass()->IsResolved());
-  UpdateMethodsCodeImpl(method, new_code);
+  UpdateMethodsCodeImpl(method, quick_code);
 }
 
 void Instrumentation::UpdateMethodsCodeToInterpreterEntryPoint(ArtMethod* method) {
@@ -1106,12 +1018,12 @@
 }
 
 void Instrumentation::UpdateMethodsCodeForJavaDebuggable(ArtMethod* method,
-                                                         const void* new_code) {
+                                                         const void* quick_code) {
   // When the runtime is set to Java debuggable, we may update the entry points of
   // all methods of a class to the interpreter bridge. A method's declaring class
   // might not be in resolved state yet in that case, so we bypass the DCHECK in
   // UpdateMethodsCode.
-  UpdateMethodsCodeImpl(method, new_code);
+  UpdateMethodsCodeImpl(method, quick_code);
 }
 
 bool Instrumentation::AddDeoptimizedMethod(ArtMethod* method) {
@@ -1162,7 +1074,7 @@
         << " is already deoptimized";
   }
   if (!InterpreterStubsInstalled()) {
-    UpdateEntryPoints(method, GetQuickToInterpreterBridge());
+    UpdateEntrypoints(method, GetQuickToInterpreterBridge());
 
     // Install instrumentation exit stub and instrumentation frames. We may already have installed
     // these previously so it will only cover the newly created frames.
@@ -1196,12 +1108,12 @@
   if (!InterpreterStubsInstalled()) {
     // Restore its code or resolution trampoline.
     if (InterpretOnly(method)) {
-      UpdateEntryPoints(method, GetQuickToInterpreterBridge());
+      UpdateEntrypoints(method, GetQuickToInterpreterBridge());
     } else if (NeedsClinitCheckBeforeCall(method) &&
                !method->GetDeclaringClass()->IsVisiblyInitialized()) {
-      UpdateEntryPoints(method, GetQuickResolutionStub());
+      UpdateEntrypoints(method, GetQuickResolutionStub());
     } else {
-      UpdateEntryPoints(method, GetOptimizedCodeFor(method));
+      UpdateEntrypoints(method, GetOptimizedCodeFor(method));
     }
 
     // If there is no deoptimized method left, we can restore the stack of each thread.
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 5209379..f66ff84 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -305,19 +305,12 @@
                !Locks::runtime_shutdown_lock_);
   void ResetQuickAllocEntryPoints() REQUIRES(Locks::runtime_shutdown_lock_);
 
-  // Returns a string representation of the given entry point.
-  static std::string EntryPointString(const void* code);
-
-  // Initialize the entrypoint of the method .`aot_code` is the AOT code.
-  void InitializeMethodsCode(ArtMethod* method, const void* aot_code)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Update the code of a method respecting any installed stubs.
-  void UpdateMethodsCode(ArtMethod* method, const void* new_code)
+  void UpdateMethodsCode(ArtMethod* method, const void* quick_code)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
 
   // Update the code of a native method to a JITed stub.
-  void UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* new_code)
+  void UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* quick_code)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
 
   // Update the code of a method to the interpreter respecting any installed stubs from debugger.
@@ -325,7 +318,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
 
   // Update the code of a method respecting any installed stubs from debugger.
-  void UpdateMethodsCodeForJavaDebuggable(ArtMethod* method, const void* new_code)
+  void UpdateMethodsCodeForJavaDebuggable(ArtMethod* method, const void* quick_code)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
 
   // Return the code that we can execute for an invoke including from the JIT.
@@ -657,7 +650,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
   bool IsDeoptimizedMethodsEmpty() const
       REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
-  void UpdateMethodsCodeImpl(ArtMethod* method, const void* new_code)
+  void UpdateMethodsCodeImpl(ArtMethod* method, const void* quick_code)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
 
   ReaderWriterMutex* GetDeoptimizedMethodsLock() const {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 937a3b0..da8915e 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -67,36 +67,25 @@
   return true;
 }
 
-bool ShouldStayInSwitchInterpreter(ArtMethod* method)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (!Runtime::Current()->IsStarted()) {
-    // For unstarted runtimes, always use the interpreter entrypoint. This fixes the case where
-    // we are doing cross compilation. Note that GetEntryPointFromQuickCompiledCode doesn't use
-    // the image pointer size here and this may case an overflow if it is called from the
-    // compiler. b/62402160
-    return true;
-  }
-
-  if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) {
+bool UseFastInterpreterToInterpreterInvoke(ArtMethod* method) {
+  Runtime* runtime = Runtime::Current();
+  const void* quick_code = method->GetEntryPointFromQuickCompiledCode();
+  if (!runtime->GetClassLinker()->IsQuickToInterpreterBridge(quick_code)) {
     return false;
   }
-
-  if (Thread::Current()->IsForceInterpreter()) {
-    // Force the use of interpreter when it is required by the debugger.
-    return true;
+  if (!method->SkipAccessChecks() || method->IsNative() || method->IsProxyMethod()) {
+    return false;
   }
-
-  if (Thread::Current()->IsAsyncExceptionPending()) {
-    // Force use of interpreter to handle async-exceptions
-    return true;
+  if (method->IsIntrinsic()) {
+    return false;
   }
-
-  const void* code = method->GetEntryPointFromQuickCompiledCode();
-  if (code == GetQuickInstrumentationEntryPoint()) {
-    code = Runtime::Current()->GetInstrumentation()->GetCodeForInvoke(method);
+  if (method->GetDeclaringClass()->IsStringClass() && method->IsConstructor()) {
+    return false;
   }
-
-  return Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(code);
+  if (method->IsStatic() && !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+    return false;
+  }
+  return true;
 }
 
 template <typename T>
@@ -1227,7 +1216,13 @@
   // PerformCall. A deoptimization could occur at any time, and we shouldn't change which
   // entrypoint to use once we start building the shadow frame.
 
-  const bool use_interpreter_entrypoint = ShouldStayInSwitchInterpreter(called_method);
+  // For unstarted runtimes, always use the interpreter entrypoint. This fixes the case where we are
+  // doing cross compilation. Note that GetEntryPointFromQuickCompiledCode doesn't use the image
+  // pointer size here and this may case an overflow if it is called from the compiler. b/62402160
+  const bool use_interpreter_entrypoint = !Runtime::Current()->IsStarted() ||
+      ClassLinker::ShouldUseInterpreterEntrypoint(
+          called_method,
+          called_method->GetEntryPointFromQuickCompiledCode());
   if (LIKELY(accessor.HasCodeItem())) {
     // When transitioning to compiled code, space only needs to be reserved for the input registers.
     // The rest of the frame gets discarded. This also prevents accessing the called method's code
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 1809227..327594b 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -127,8 +127,7 @@
 bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
             const Instruction* inst, uint16_t inst_data, JValue* result);
 
-// Called by the switch interpreter to know if we can stay in it.
-bool ShouldStayInSwitchInterpreter(ArtMethod* method)
+bool UseFastInterpreterToInterpreterInvoke(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // Throws exception if we are getting close to the end of the stack.
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 76ad139..19f43f9 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -514,13 +514,15 @@
     }
   }
 
+  bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
+      called_method, called_method->GetEntryPointFromQuickCompiledCode());
   PerformCall(self,
               accessor,
               shadow_frame.GetMethod(),
               first_dest_reg,
               new_shadow_frame,
               result,
-              interpreter::ShouldStayInSwitchInterpreter(called_method));
+              use_interpreter_entrypoint);
   if (self->IsExceptionPending()) {
     return false;
   }
@@ -609,13 +611,15 @@
   new_shadow_frame->SetVRegReference(0, receiver.Get());
   new_shadow_frame->SetVRegReference(1, sf.Get());
 
+  bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
+      called_method, called_method->GetEntryPointFromQuickCompiledCode());
   PerformCall(self,
               accessor,
               shadow_frame.GetMethod(),
               0 /* first destination register */,
               new_shadow_frame,
               result,
-              interpreter::ShouldStayInSwitchInterpreter(called_method));
+              use_interpreter_entrypoint);
   if (self->IsExceptionPending()) {
     return false;
   }
@@ -1228,13 +1232,15 @@
                                first_dest_reg);
   self->EndAssertNoThreadSuspension(old_cause);
 
+  bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
+      called_method, called_method->GetEntryPointFromQuickCompiledCode());
   PerformCall(self,
               accessor,
               called_method,
               first_dest_reg,
               new_shadow_frame,
               result,
-              interpreter::ShouldStayInSwitchInterpreter(called_method));
+              use_interpreter_entrypoint);
   if (self->IsExceptionPending()) {
     return false;
   }
diff --git a/runtime/stack.cc b/runtime/stack.cc
index e0b87de..7f1f47f 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -682,7 +682,6 @@
   uintptr_t code_start = reinterpret_cast<uintptr_t>(code);
   CHECK(code_start <= pc && pc <= (code_start + code_size))
       << method->PrettyMethod()
-      << " " << Runtime::Current()->GetInstrumentation()->EntryPointString(code)
       << " pc=" << std::hex << pc
       << " code_start=" << code_start
       << " code_size=" << code_size;
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 00f5825..4c22bd1 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -174,8 +174,9 @@
     return false;
   }
   const void* actual_code = method->GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize);
-  bool interpreter = (actual_code == interpreter::GetNterpEntryPoint()) ||
-      Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(actual_code);
+  bool interpreter =
+      Runtime::Current()->GetClassLinker()->ShouldUseInterpreterEntrypoint(method, actual_code) ||
+      (actual_code == interpreter::GetNterpEntryPoint());
   return !interpreter;
 }