Faster `ClassLinker::LoadMethod()`.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 181943478
Change-Id: I86a34a5e0037774aa5bfe5ace3e2c0c8d1b92924
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 6cfd5c8..3f0de3b 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3583,7 +3583,7 @@
}, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index,
image_pointer_size_);
- LoadMethod(dex_file, method, klass, art_method);
+ LoadMethod(dex_file, method, klass.Get(), art_method);
LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
uint32_t it_method_index = method.GetIndex();
if (last_dex_method_index == it_method_index) {
@@ -3601,7 +3601,7 @@
class_def_method_index - accessor.NumDirectMethods(),
image_pointer_size_);
art_method->ResetCounter(hotness_threshold);
- LoadMethod(dex_file, method, klass, art_method);
+ LoadMethod(dex_file, method, klass.Get(), art_method);
LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
++class_def_method_index;
});
@@ -3643,43 +3643,54 @@
void ClassLinker::LoadMethod(const DexFile& dex_file,
const ClassAccessor::Method& method,
- Handle<mirror::Class> klass,
+ ObjPtr<mirror::Class> klass,
ArtMethod* dst) {
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+
const uint32_t dex_method_idx = method.GetIndex();
const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
- const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);
+ uint32_t name_utf16_length;
+ const char* method_name = dex_file.StringDataAndUtf16LengthByIdx(method_id.name_idx_,
+ &name_utf16_length);
+ std::string_view shorty = dex_file.GetShortyView(dex_file.GetProtoId(method_id.proto_idx_));
- ScopedAssertNoThreadSuspension ants("LoadMethod");
dst->SetDexMethodIndex(dex_method_idx);
- dst->SetDeclaringClass(klass.Get());
+ dst->SetDeclaringClass(klass);
// Get access flags from the DexFile and set hiddenapi runtime access flags.
uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method);
- if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
+ auto has_ascii_name = [method_name, name_utf16_length](const char* ascii_name,
+ size_t length) ALWAYS_INLINE {
+ DCHECK_EQ(strlen(ascii_name), length);
+ return length == name_utf16_length &&
+ method_name[length] == 0 && // Is `method_name` an ASCII string?
+ memcmp(ascii_name, method_name, length) == 0;
+ };
+ if (UNLIKELY(has_ascii_name("finalize", sizeof("finalize") - 1u))) {
// Set finalizable flag on declaring class.
- if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) {
+ if (shorty == "V") {
// Void return type.
if (klass->GetClassLoader() != nullptr) { // All non-boot finalizer methods are flagged.
klass->SetFinalizable();
} else {
- std::string temp;
- const char* klass_descriptor = klass->GetDescriptor(&temp);
+ std::string_view klass_descriptor =
+ dex_file.GetTypeDescriptorView(dex_file.GetTypeId(klass->GetDexTypeIndex()));
// The Enum class declares a "final" finalize() method to prevent subclasses from
// introducing a finalizer. We don't want to set the finalizable flag for Enum or its
// subclasses, so we exclude it here.
// We also want to avoid setting the flag on Object, where we know that finalize() is
// empty.
- if (strcmp(klass_descriptor, "Ljava/lang/Object;") != 0 &&
- strcmp(klass_descriptor, "Ljava/lang/Enum;") != 0) {
+ if (klass_descriptor != "Ljava/lang/Object;" &&
+ klass_descriptor != "Ljava/lang/Enum;") {
klass->SetFinalizable();
}
}
}
} else if (method_name[0] == '<') {
// Fix broken access flags for initializers. Bug 11157540.
- bool is_init = (strcmp("<init>", method_name) == 0);
- bool is_clinit = !is_init && (strcmp("<clinit>", method_name) == 0);
+ bool is_init = has_ascii_name("<init>", sizeof("<init>") - 1u);
+ bool is_clinit = has_ascii_name("<clinit>", sizeof("<clinit>") - 1u);
if (UNLIKELY(!is_init && !is_clinit)) {
LOG(WARNING) << "Unexpected '<' at start of method name " << method_name;
} else {
@@ -3690,54 +3701,54 @@
}
}
}
+ size_t slow_args_search_start = 1u; // First arg.
if (UNLIKELY((access_flags & kAccNative) != 0u)) {
// Check if the native method is annotated with @FastNative or @CriticalNative.
- access_flags |= annotations::GetNativeMethodAnnotationAccessFlags(
- dex_file, dst->GetClassDef(), dex_method_idx);
- } else if ((access_flags & kAccAbstract) == 0u &&
- annotations::MethodIsNeverCompile(dex_file, dst->GetClassDef(), dex_method_idx)) {
- access_flags |= kAccCompileDontBother;
- }
- dst->SetAccessFlags(access_flags);
- // Must be done after SetAccessFlags since IsAbstract depends on it.
- if (klass->IsInterface() && dst->IsAbstract()) {
- dst->CalculateAndSetImtIndex();
- }
- if (dst->HasCodeItem()) {
- DCHECK_NE(method.GetCodeItemOffset(), 0u);
- if (Runtime::Current()->IsAotCompiler()) {
- dst->SetDataPtrSize(reinterpret_cast32<void*>(method.GetCodeItemOffset()), image_pointer_size_);
- } else {
- dst->SetCodeItem(dst->GetDexFile()->GetCodeItem(method.GetCodeItemOffset()),
- dst->GetDexFile()->IsCompactDexFile());
- }
- } else {
- dst->SetDataPtrSize(nullptr, image_pointer_size_);
+ const dex::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex());
+ access_flags |=
+ annotations::GetNativeMethodAnnotationAccessFlags(dex_file, class_def, dex_method_idx);
+ dst->SetAccessFlags(access_flags);
+ DCHECK(!dst->IsAbstract());
+ DCHECK(!dst->HasCodeItem());
DCHECK_EQ(method.GetCodeItemOffset(), 0u);
- }
-
- // Set optimization flags related to the shorty.
- uint32_t shorty_length;
- const char* shorty = dst->GetShorty(&shorty_length);
- bool all_parameters_are_reference = true;
- bool all_parameters_are_reference_or_int = true;
- bool return_type_is_fp = (shorty[0] == 'F' || shorty[0] == 'D');
-
- for (size_t i = 1; i < shorty_length; ++i) {
- if (shorty[i] != 'L') {
- all_parameters_are_reference = false;
- if (shorty[i] == 'F' || shorty[i] == 'D' || shorty[i] == 'J') {
- all_parameters_are_reference_or_int = false;
- break;
- }
+ dst->SetDataPtrSize(nullptr, image_pointer_size_); // JNI stub/trampoline not linked yet.
+ } else if ((access_flags & kAccAbstract) != 0u) {
+ dst->SetAccessFlags(access_flags);
+ // Must be done after SetAccessFlags since IsAbstract depends on it.
+ DCHECK(dst->IsAbstract());
+ if (klass->IsInterface()) {
+ dst->CalculateAndSetImtIndex();
+ }
+ DCHECK(!dst->HasCodeItem());
+ DCHECK_EQ(method.GetCodeItemOffset(), 0u);
+ dst->SetDataPtrSize(nullptr, image_pointer_size_); // Single implementation not set yet.
+ } else {
+ const dex::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex());
+ if (annotations::MethodIsNeverCompile(dex_file, class_def, dex_method_idx)) {
+ access_flags |= kAccCompileDontBother;
+ }
+ dst->SetAccessFlags(access_flags);
+ DCHECK(!dst->IsAbstract());
+ DCHECK(dst->HasCodeItem());
+ uint32_t code_item_offset = method.GetCodeItemOffset();
+ DCHECK_NE(code_item_offset, 0u);
+ if (Runtime::Current()->IsAotCompiler()) {
+ dst->SetDataPtrSize(reinterpret_cast32<void*>(code_item_offset), image_pointer_size_);
+ } else {
+ dst->SetCodeItem(dex_file.GetCodeItem(code_item_offset), dex_file.IsCompactDexFile());
+ }
+ // Check for nterp entry fast-path based on shorty.
+ slow_args_search_start = shorty.find_first_not_of('L', 1u);
+ if (slow_args_search_start == std::string_view::npos) {
+ dst->SetNterpEntryPointFastPathFlag();
}
}
- if (!dst->IsNative() && all_parameters_are_reference) {
- dst->SetNterpEntryPointFastPathFlag();
- }
-
- if (!return_type_is_fp && all_parameters_are_reference_or_int) {
+ // Check for nterp invoke fast-path based on shorty.
+ auto is_slow_arg = [](char c) { return c == 'F' || c == 'D' || c == 'J'; };
+ if ((shorty[0] != 'F' && shorty[0] != 'D') && // Returns reference or integral type.
+ (slow_args_search_start == std::string_view::npos ||
+ std::none_of(shorty.begin() + slow_args_search_start, shorty.end(), is_slow_arg))) {
dst->SetNterpInvokeFastPathFlag();
}
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a503946..02fce02 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -975,7 +975,7 @@
void LoadMethod(const DexFile& dex_file,
const ClassAccessor::Method& method,
- Handle<mirror::Class> klass,
+ ObjPtr<mirror::Class> klass,
ArtMethod* dst)
REQUIRES_SHARED(Locks::mutator_lock_);