Inline ResolveMethod and ResolveField in nterp
They are on very hot path (field and method opcodes).
This makes up startup ~1% faster with interpreter.
The methods are fairly big and spill most registers,
but the DexCache-hit fastpath requires no spills.
Rather than refactoring the methods, it is easier
to just inline them, since the caller already spills.
Test: test.py -b --host
Change-Id: I60221e3aa819378ed2f353044cfdfe6cbc51237d
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 4e8f8ed..26c422b 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -401,6 +401,104 @@
return resolved_method;
}
+template <ClassLinker::ResolveMode kResolveMode>
+inline ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ ArtMethod* referrer,
+ InvokeType type) {
+ DCHECK(dex_cache->GetClassLoader() == class_loader.Get());
+ DCHECK(!Thread::Current()->IsExceptionPending()) << Thread::Current()->GetException()->Dump();
+ DCHECK(dex_cache != nullptr);
+ DCHECK(referrer == nullptr || !referrer->IsProxyMethod());
+ // Check for hit in the dex cache.
+ ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx);
+ Thread::PoisonObjectPointersIfDebug();
+ DCHECK(resolved == nullptr || !resolved->IsRuntimeMethod());
+ bool valid_dex_cache_method = resolved != nullptr;
+ if (kResolveMode == ResolveMode::kNoChecks && valid_dex_cache_method) {
+ // We have a valid method from the DexCache and no checks to perform.
+ DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
+ return resolved;
+ }
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ const dex::MethodId& method_id = dex_file.GetMethodId(method_idx);
+ ObjPtr<mirror::Class> klass = nullptr;
+ if (valid_dex_cache_method) {
+ // We have a valid method from the DexCache but we need to perform ICCE and IAE checks.
+ DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
+ klass = LookupResolvedType(method_id.class_idx_, dex_cache.Get(), class_loader.Get());
+ if (UNLIKELY(klass == nullptr)) {
+ // We normaly should not end up here. However the verifier currently doesn't guarantee
+ // the invariant of having the klass in the class table. b/73760543
+ klass = ResolveType(method_id.class_idx_, dex_cache, class_loader);
+ if (klass == nullptr) {
+ // This can only happen if the current thread is not allowed to load
+ // classes.
+ DCHECK(!Thread::Current()->CanLoadClasses());
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
+ }
+ }
+ } else {
+ // The method was not in the DexCache, resolve the declaring class.
+ klass = ResolveType(method_id.class_idx_, dex_cache, class_loader);
+ if (klass == nullptr) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
+ }
+ }
+
+ // Check if the invoke type matches the class type.
+ if (kResolveMode == ResolveMode::kCheckICCEAndIAE &&
+ CheckInvokeClassMismatch</* kThrow= */ true>(
+ dex_cache.Get(), type, [klass]() { return klass; })) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
+ }
+
+ if (!valid_dex_cache_method) {
+ resolved = FindResolvedMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx);
+ }
+
+ // Note: We can check for IllegalAccessError only if we have a referrer.
+ if (kResolveMode == ResolveMode::kCheckICCEAndIAE && resolved != nullptr && referrer != nullptr) {
+ ObjPtr<mirror::Class> methods_class = resolved->GetDeclaringClass();
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+ if (!referring_class->CheckResolvedMethodAccess(methods_class,
+ resolved,
+ dex_cache.Get(),
+ method_idx,
+ type)) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
+ }
+ }
+
+ // If we found a method, check for incompatible class changes.
+ if (LIKELY(resolved != nullptr) &&
+ LIKELY(kResolveMode == ResolveMode::kNoChecks ||
+ !resolved->CheckIncompatibleClassChange(type))) {
+ return resolved;
+ } else {
+ // If we had a method, or if we can find one with another lookup type,
+ // it's an incompatible-class-change error.
+ if (resolved == nullptr) {
+ resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx);
+ }
+ if (resolved != nullptr) {
+ ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);
+ } else {
+ // We failed to find the method (using all lookup types), so throw a NoSuchMethodError.
+ const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
+ const Signature signature = dex_file.GetMethodSignature(method_id);
+ ThrowNoSuchMethodError(type, klass, name, signature);
+ }
+ Thread::Current()->AssertPendingException();
+ return nullptr;
+ }
+}
+
inline ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx,
ArtMethod* referrer,
bool is_static) {
@@ -431,6 +529,35 @@
return resolved_field;
}
+inline ArtField* ClassLinker::ResolveField(uint32_t field_idx,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ bool is_static) {
+ DCHECK(dex_cache != nullptr);
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Get());
+ DCHECK(!Thread::Current()->IsExceptionPending()) << Thread::Current()->GetException()->Dump();
+ ArtField* resolved = dex_cache->GetResolvedField(field_idx);
+ Thread::PoisonObjectPointersIfDebug();
+ if (resolved != nullptr) {
+ return resolved;
+ }
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ const dex::FieldId& field_id = dex_file.GetFieldId(field_idx);
+ ObjPtr<mirror::Class> klass = ResolveType(field_id.class_idx_, dex_cache, class_loader);
+ if (klass == nullptr) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
+ }
+
+ resolved = FindResolvedField(klass, dex_cache.Get(), class_loader.Get(), field_idx, is_static);
+ if (resolved == nullptr) {
+ const char* name = dex_file.GetFieldName(field_id);
+ const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+ ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name);
+ }
+ return resolved;
+}
+
template <class Visitor>
inline void ClassLinker::VisitClassTables(const Visitor& visitor) {
Thread* const self = Thread::Current();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 69e87d3..8065238 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -9054,104 +9054,6 @@
}
}
-template <ClassLinker::ResolveMode kResolveMode>
-ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader,
- ArtMethod* referrer,
- InvokeType type) {
- DCHECK(dex_cache->GetClassLoader() == class_loader.Get());
- DCHECK(!Thread::Current()->IsExceptionPending()) << Thread::Current()->GetException()->Dump();
- DCHECK(dex_cache != nullptr);
- DCHECK(referrer == nullptr || !referrer->IsProxyMethod());
- // Check for hit in the dex cache.
- ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx);
- Thread::PoisonObjectPointersIfDebug();
- DCHECK(resolved == nullptr || !resolved->IsRuntimeMethod());
- bool valid_dex_cache_method = resolved != nullptr;
- if (kResolveMode == ResolveMode::kNoChecks && valid_dex_cache_method) {
- // We have a valid method from the DexCache and no checks to perform.
- DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
- return resolved;
- }
- const DexFile& dex_file = *dex_cache->GetDexFile();
- const dex::MethodId& method_id = dex_file.GetMethodId(method_idx);
- ObjPtr<mirror::Class> klass = nullptr;
- if (valid_dex_cache_method) {
- // We have a valid method from the DexCache but we need to perform ICCE and IAE checks.
- DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
- klass = LookupResolvedType(method_id.class_idx_, dex_cache.Get(), class_loader.Get());
- if (UNLIKELY(klass == nullptr)) {
- // We normaly should not end up here. However the verifier currently doesn't guarantee
- // the invariant of having the klass in the class table. b/73760543
- klass = ResolveType(method_id.class_idx_, dex_cache, class_loader);
- if (klass == nullptr) {
- // This can only happen if the current thread is not allowed to load
- // classes.
- DCHECK(!Thread::Current()->CanLoadClasses());
- DCHECK(Thread::Current()->IsExceptionPending());
- return nullptr;
- }
- }
- } else {
- // The method was not in the DexCache, resolve the declaring class.
- klass = ResolveType(method_id.class_idx_, dex_cache, class_loader);
- if (klass == nullptr) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return nullptr;
- }
- }
-
- // Check if the invoke type matches the class type.
- if (kResolveMode == ResolveMode::kCheckICCEAndIAE &&
- CheckInvokeClassMismatch</* kThrow= */ true>(
- dex_cache.Get(), type, [klass]() { return klass; })) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return nullptr;
- }
-
- if (!valid_dex_cache_method) {
- resolved = FindResolvedMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx);
- }
-
- // Note: We can check for IllegalAccessError only if we have a referrer.
- if (kResolveMode == ResolveMode::kCheckICCEAndIAE && resolved != nullptr && referrer != nullptr) {
- ObjPtr<mirror::Class> methods_class = resolved->GetDeclaringClass();
- ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
- if (!referring_class->CheckResolvedMethodAccess(methods_class,
- resolved,
- dex_cache.Get(),
- method_idx,
- type)) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return nullptr;
- }
- }
-
- // If we found a method, check for incompatible class changes.
- if (LIKELY(resolved != nullptr) &&
- LIKELY(kResolveMode == ResolveMode::kNoChecks ||
- !resolved->CheckIncompatibleClassChange(type))) {
- return resolved;
- } else {
- // If we had a method, or if we can find one with another lookup type,
- // it's an incompatible-class-change error.
- if (resolved == nullptr) {
- resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx);
- }
- if (resolved != nullptr) {
- ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);
- } else {
- // We failed to find the method (using all lookup types), so throw a NoSuchMethodError.
- const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
- const Signature signature = dex_file.GetMethodSignature(method_id);
- ThrowNoSuchMethodError(type, klass, name, signature);
- }
- Thread::Current()->AssertPendingException();
- return nullptr;
- }
-}
-
ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
@@ -9205,35 +9107,6 @@
return FindResolvedField(klass, dex_cache, class_loader, field_idx, is_static);
}
-ArtField* ClassLinker::ResolveField(uint32_t field_idx,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader,
- bool is_static) {
- DCHECK(dex_cache != nullptr);
- DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Get());
- DCHECK(!Thread::Current()->IsExceptionPending()) << Thread::Current()->GetException()->Dump();
- ArtField* resolved = dex_cache->GetResolvedField(field_idx);
- Thread::PoisonObjectPointersIfDebug();
- if (resolved != nullptr) {
- return resolved;
- }
- const DexFile& dex_file = *dex_cache->GetDexFile();
- const dex::FieldId& field_id = dex_file.GetFieldId(field_idx);
- ObjPtr<mirror::Class> klass = ResolveType(field_id.class_idx_, dex_cache, class_loader);
- if (klass == nullptr) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return nullptr;
- }
-
- resolved = FindResolvedField(klass, dex_cache.Get(), class_loader.Get(), field_idx, is_static);
- if (resolved == nullptr) {
- const char* name = dex_file.GetFieldName(field_id);
- const char* type = dex_file.GetFieldTypeDescriptor(field_id);
- ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name);
- }
- return resolved;
-}
-
ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
@@ -10172,20 +10045,6 @@
UNREACHABLE();
}
-// Instantiate ClassLinker::ResolveMethod.
-template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
- uint32_t method_idx,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader,
- ArtMethod* referrer,
- InvokeType type);
-template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
- uint32_t method_idx,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader,
- ArtMethod* referrer,
- InvokeType type);
-
// Instantiate ClassLinker::AllocClass.
template ObjPtr<mirror::Class> ClassLinker::AllocClass</* kMovable= */ true>(
Thread* self,
diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc
index ddef31d..fd57ece 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -20,6 +20,7 @@
#include "nterp.h"
#include "base/quasi_atomic.h"
+#include "class_linker-inl.h"
#include "dex/dex_instruction_utils.h"
#include "debugger.h"
#include "entrypoints/entrypoint_utils-inl.h"
@@ -264,6 +265,7 @@
return dex_file->GetShorty(proto_idx);
}
+FLATTEN
extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
UpdateHotness(caller);
@@ -422,6 +424,7 @@
}
}
+FLATTEN
static ArtField* ResolveFieldWithAccessChecks(Thread* self,
ClassLinker* class_linker,
uint16_t field_index,