Reland "More entrypoint handling cleanup."
This reverts commit 8cedd8b45854cb971510a435909573a5855092d2.
Reason for revert: Fixed test
Change-Id: I551912891b65cac9927dcdb89033113f290d913c
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index e227aa2..82b1cc2 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3309,66 +3309,6 @@
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();
@@ -3395,17 +3335,12 @@
}
}
Runtime* runtime = Runtime::Current();
- if (!runtime->IsStarted()) {
- if (runtime->IsAotCompiler() || runtime->GetHeap()->HasBootImageSpace()) {
- return; // OAT file unavailable.
- }
+ if (runtime->IsAotCompiler()) {
+ // We should not update entrypoints when running the transactional
+ // interpreter.
+ return;
}
- 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);
@@ -3413,42 +3348,8 @@
// Only update static methods.
continue;
}
- 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);
+ instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
+ instrumentation->UpdateMethodsCode(method, instrumentation->GetCodeForInvoke(method));
}
// Ignore virtual methods on the iterator.
}
@@ -3477,6 +3378,7 @@
// 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);
@@ -3490,46 +3392,13 @@
const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);
quick_code = oat_method.GetQuickCode();
}
-
- 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);
- }
+ runtime->GetInstrumentation()->InitializeMethodsCode(method, 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 0d32271..c40cea3 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -718,9 +718,6 @@
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 f1b52e0..d3e631c 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,7 +226,11 @@
}
}
}
- method->SetEntryPointFromQuickCompiledCode(quick_code);
+ // 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);
+ }
}
bool Instrumentation::CodeNeedsEntryExitStub(const void* code, ArtMethod* method) {
@@ -289,6 +293,35 @@
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();
@@ -297,12 +330,10 @@
}
// In debuggable mode, we can only use AOT code for native methods.
- 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;
- }
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const void* aot_code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize());
+ if (CanUseAotCode(method, aot_code)) {
+ return aot_code;
}
// If the method has been precompiled, there can be a JIT version.
@@ -314,13 +345,67 @@
}
}
- if (interpreter::CanRuntimeUseNterp() && CanMethodUseNterp(method)) {
+ // 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()) {
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.
@@ -336,14 +421,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;
}
@@ -352,10 +437,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
@@ -943,7 +1028,7 @@
}
}
-static std::string EntrypointString(const void* code) {
+std::string Instrumentation::EntryPointString(const void* code) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
jit::Jit* jit = Runtime::Current()->GetJit();
if (class_linker->IsQuickToInterpreterBridge(code)) {
@@ -966,38 +1051,41 @@
return "unknown";
}
-void Instrumentation::UpdateMethodsCodeImpl(ArtMethod* method, const void* quick_code) {
+void Instrumentation::UpdateMethodsCodeImpl(ArtMethod* method, const void* new_code) {
if (!EntryExitStubsInstalled()) {
// Fast path: no instrumentation.
DCHECK(!IsDeoptimized(method));
- UpdateEntrypoints(method, quick_code);
+ UpdateEntryPoints(method, new_code);
return;
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (class_linker->IsQuickToInterpreterBridge(quick_code)) {
+ if (class_linker->IsQuickToInterpreterBridge(new_code)) {
// It's always OK to update to the interpreter.
- UpdateEntrypoints(method, quick_code);
+ UpdateEntryPoints(method, new_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(quick_code, method)) {
+ if (CodeNeedsEntryExitStub(new_code, method)) {
+ DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickInstrumentationEntryPoint() ||
+ class_linker->IsQuickToInterpreterBridge(method->GetEntryPointFromQuickCompiledCode()))
+ << EntryPointString(method->GetEntryPointFromQuickCompiledCode());
// 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, quick_code);
+ UpdateEntryPoints(method, new_code);
}
-void Instrumentation::UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* quick_code) {
+void Instrumentation::UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* new_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.
@@ -1005,12 +1093,12 @@
// If stubs are installed don't update.
return;
}
- UpdateEntrypoints(method, quick_code);
+ UpdateEntryPoints(method, new_code);
}
-void Instrumentation::UpdateMethodsCode(ArtMethod* method, const void* quick_code) {
+void Instrumentation::UpdateMethodsCode(ArtMethod* method, const void* new_code) {
DCHECK(method->GetDeclaringClass()->IsResolved());
- UpdateMethodsCodeImpl(method, quick_code);
+ UpdateMethodsCodeImpl(method, new_code);
}
void Instrumentation::UpdateMethodsCodeToInterpreterEntryPoint(ArtMethod* method) {
@@ -1018,12 +1106,12 @@
}
void Instrumentation::UpdateMethodsCodeForJavaDebuggable(ArtMethod* method,
- const void* quick_code) {
+ const void* new_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, quick_code);
+ UpdateMethodsCodeImpl(method, new_code);
}
bool Instrumentation::AddDeoptimizedMethod(ArtMethod* method) {
@@ -1074,7 +1162,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.
@@ -1108,12 +1196,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 f66ff84..5209379 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -305,12 +305,19 @@
!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* quick_code)
+ void UpdateMethodsCode(ArtMethod* method, const void* new_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* quick_code)
+ void UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* new_code)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Update the code of a method to the interpreter respecting any installed stubs from debugger.
@@ -318,7 +325,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* quick_code)
+ void UpdateMethodsCodeForJavaDebuggable(ArtMethod* method, const void* new_code)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Return the code that we can execute for an invoke including from the JIT.
@@ -650,7 +657,7 @@
REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
bool IsDeoptimizedMethodsEmpty() const
REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
- void UpdateMethodsCodeImpl(ArtMethod* method, const void* quick_code)
+ void UpdateMethodsCodeImpl(ArtMethod* method, const void* new_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 da8915e..937a3b0 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -67,25 +67,36 @@
return true;
}
-bool UseFastInterpreterToInterpreterInvoke(ArtMethod* method) {
- Runtime* runtime = Runtime::Current();
- const void* quick_code = method->GetEntryPointFromQuickCompiledCode();
- if (!runtime->GetClassLinker()->IsQuickToInterpreterBridge(quick_code)) {
+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())) {
return false;
}
- if (!method->SkipAccessChecks() || method->IsNative() || method->IsProxyMethod()) {
- return false;
+
+ if (Thread::Current()->IsForceInterpreter()) {
+ // Force the use of interpreter when it is required by the debugger.
+ return true;
}
- if (method->IsIntrinsic()) {
- return false;
+
+ if (Thread::Current()->IsAsyncExceptionPending()) {
+ // Force use of interpreter to handle async-exceptions
+ return true;
}
- if (method->GetDeclaringClass()->IsStringClass() && method->IsConstructor()) {
- return false;
+
+ const void* code = method->GetEntryPointFromQuickCompiledCode();
+ if (code == GetQuickInstrumentationEntryPoint()) {
+ code = Runtime::Current()->GetInstrumentation()->GetCodeForInvoke(method);
}
- if (method->IsStatic() && !method->GetDeclaringClass()->IsVisiblyInitialized()) {
- return false;
- }
- return true;
+
+ return Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(code);
}
template <typename T>
@@ -1216,13 +1227,7 @@
// PerformCall. A deoptimization could occur at any time, and we shouldn't change which
// entrypoint to use once we start building the shadow frame.
- // 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());
+ const bool use_interpreter_entrypoint = ShouldStayInSwitchInterpreter(called_method);
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 327594b..1809227 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -127,7 +127,8 @@
bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
const Instruction* inst, uint16_t inst_data, JValue* result);
-bool UseFastInterpreterToInterpreterInvoke(ArtMethod* method)
+// Called by the switch interpreter to know if we can stay in it.
+bool ShouldStayInSwitchInterpreter(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 19f43f9..76ad139 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -514,15 +514,13 @@
}
}
- bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
- called_method, called_method->GetEntryPointFromQuickCompiledCode());
PerformCall(self,
accessor,
shadow_frame.GetMethod(),
first_dest_reg,
new_shadow_frame,
result,
- use_interpreter_entrypoint);
+ interpreter::ShouldStayInSwitchInterpreter(called_method));
if (self->IsExceptionPending()) {
return false;
}
@@ -611,15 +609,13 @@
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,
- use_interpreter_entrypoint);
+ interpreter::ShouldStayInSwitchInterpreter(called_method));
if (self->IsExceptionPending()) {
return false;
}
@@ -1232,15 +1228,13 @@
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,
- use_interpreter_entrypoint);
+ interpreter::ShouldStayInSwitchInterpreter(called_method));
if (self->IsExceptionPending()) {
return false;
}
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 7f1f47f..e0b87de 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -682,6 +682,7 @@
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;