diff options
| -rw-r--r-- | runtime/class_linker.cc | 147 | ||||
| -rw-r--r-- | runtime/class_linker.h | 3 | ||||
| -rw-r--r-- | runtime/instrumentation.cc | 150 | ||||
| -rw-r--r-- | runtime/instrumentation.h | 15 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_common.cc | 45 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_common.h | 3 | ||||
| -rw-r--r-- | runtime/method_handles.cc | 12 | ||||
| -rw-r--r-- | runtime/stack.cc | 1 | ||||
| -rw-r--r-- | test/common/runtime_state.cc | 5 |
9 files changed, 171 insertions, 210 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e227aa2650..82b1cc2551 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3309,66 +3309,6 @@ uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, 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 @@ void ClassLinker::FixupStaticTrampolines(Thread* self, ObjPtr<mirror::Class> kla } } 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 @@ void ClassLinker::FixupStaticTrampolines(Thread* self, ObjPtr<mirror::Class> kla // 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 @@ static void LinkCode(ClassLinker* class_linker, // 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 @@ static void LinkCode(ClassLinker* class_linker, 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 0d32271389..c40cea334a 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -718,9 +718,6 @@ class ClassLinker { 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 f1b52e0a40..d3e631cd64 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -211,7 +211,7 @@ static bool CanHandleInitializationCheck(const void* code) { (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 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) } } } - 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 @@ bool Instrumentation::InterpretOnly(ArtMethod* method) REQUIRES_SHARED(Locks::mu 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 @@ static const void* GetOptimizedCodeFor(ArtMethod* method) REQUIRES_SHARED(Locks: } // 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 @@ static const void* GetOptimizedCodeFor(ArtMethod* method) REQUIRES_SHARED(Locks: } } - 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 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { // 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 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { 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 @@ void Instrumentation::ResetQuickAllocEntryPoints() { } } -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 @@ static std::string EntrypointString(const void* code) { 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 @@ void Instrumentation::UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const // 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::UpdateMethodsCodeToInterpreterEntryPoint(ArtMethod* method } 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 @@ void Instrumentation::Deoptimize(ArtMethod* method) { << " 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 @@ void Instrumentation::Undeoptimize(ArtMethod* method) { 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 f66ff849f6..5209379a97 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -305,12 +305,19 @@ class Instrumentation { !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 @@ class Instrumentation { 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 @@ class Instrumentation { 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 da8915e32a..937a3b0f97 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -67,25 +67,36 @@ bool CheckStackOverflow(Thread* self, size_t frame_size) return true; } -bool UseFastInterpreterToInterpreterInvoke(ArtMethod* method) { - Runtime* runtime = Runtime::Current(); - const void* quick_code = method->GetEntryPointFromQuickCompiledCode(); - if (!runtime->GetClassLinker()->IsQuickToInterpreterBridge(quick_code)) { - return false; +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 (!method->SkipAccessChecks() || method->IsNative() || method->IsProxyMethod()) { + + if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) { return false; } - if (method->IsIntrinsic()) { - return false; + + if (Thread::Current()->IsForceInterpreter()) { + // Force the use of interpreter when it is required by the debugger. + return true; } - if (method->GetDeclaringClass()->IsStringClass() && method->IsConstructor()) { - return false; + + if (Thread::Current()->IsAsyncExceptionPending()) { + // Force use of interpreter to handle async-exceptions + return true; } - if (method->IsStatic() && !method->GetDeclaringClass()->IsVisiblyInitialized()) { - return false; + + const void* code = method->GetEntryPointFromQuickCompiledCode(); + if (code == GetQuickInstrumentationEntryPoint()) { + code = Runtime::Current()->GetInstrumentation()->GetCodeForInvoke(method); } - return true; + + return Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(code); } template <typename T> @@ -1216,13 +1227,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, // 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 327594b2bb..1809227f2c 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -127,7 +127,8 @@ template<bool is_range, bool do_assignability_check> 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 19f43f97a9..76ad139da9 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -514,15 +514,13 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, } } - 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 @@ static inline bool MethodHandleInvokeTransform(ArtMethod* called_method, 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 @@ static inline bool MethodHandleInvokeExactInternal( 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 7f1f47f2bc..e0b87de2cd 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -682,6 +682,7 @@ static void AssertPcIsWithinQuickCode(ArtMethod* method, uintptr_t pc) 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 4c22bd16e9..00f5825207 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -174,9 +174,8 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env, return false; } const void* actual_code = method->GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize); - bool interpreter = - Runtime::Current()->GetClassLinker()->ShouldUseInterpreterEntrypoint(method, actual_code) || - (actual_code == interpreter::GetNterpEntryPoint()); + bool interpreter = (actual_code == interpreter::GetNterpEntryPoint()) || + Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(actual_code); return !interpreter; } |