summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2025-02-05 11:36:53 +0000
committer VladimĂ­r Marko <vmarko@google.com> 2025-02-05 07:41:24 -0800
commitef5054636ca350b94adef6906d2d51992ca4531c (patch)
tree63db24be4c8530617bfda39dd5085e5e7bd127fa
parenta7045d8fd8a38b447b176e93c81765540863c1c9 (diff)
Refactor `ImTable::GetImtIndex()`.
Make it easier to calculate the IMT index without an actual `ArtMethod` in preparation for a significant refactoring of `ClassLinker::LoadClass()`. Update documentation of `ArtMethod::imt_index_` which is actually unused for abstract non-interface methods. Also reorder code in `ClassLinker` without any modification to make it easier to compare the differences in the upcoming refactoring. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 329196666 Change-Id: Ic71ff323ea92325958d59536e83ab70d057b94df
-rw-r--r--oatdump/oatdump.cc10
-rw-r--r--runtime/art_method.h3
-rw-r--r--runtime/class_linker.cc294
-rw-r--r--runtime/imtable-inl.h45
-rw-r--r--runtime/imtable.h9
5 files changed, 189 insertions, 172 deletions
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index ab22e36c17..b6f681a184 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -3006,10 +3006,18 @@ class IMTDumper {
for (ArtMethod& iface_method : iface->GetVirtualMethods(pointer_size)) {
uint32_t class_hash, name_hash, signature_hash;
- ImTable::GetImtHashComponents(&iface_method, &class_hash, &name_hash, &signature_hash);
+ ImTable::GetImtHashComponents(*iface_method.GetDexFile(),
+ iface_method.GetDexMethodIndex(),
+ &class_hash,
+ &name_hash,
+ &signature_hash);
uint32_t imt_slot = ImTable::GetImtIndex(&iface_method);
+ // Note: For default methods we use the dex method index for calculating the slot.
+ // For abstract methods the compile-time constant `kImTableHashUseName` determines
+ // whether we use the component hashes (current behavior) or the dex method index.
std::cerr << " " << iface_method.PrettyMethod(true)
<< " slot=" << imt_slot
+ << " dex_method_index=" << iface_method.GetDexMethodIndex()
<< std::hex
<< " class_hash=0x" << class_hash
<< " name_hash=0x" << name_hash
diff --git a/runtime/art_method.h b/runtime/art_method.h
index ee11328385..186ff7e45e 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -1126,8 +1126,9 @@ class EXPORT ArtMethod final {
// Non-abstract methods: The hotness we measure for this method. Not atomic,
// as we allow missing increments: if the method is hot, we will see it eventually.
uint16_t hotness_count_;
- // Abstract methods: IMT index.
+ // Abstract interface methods: IMT index.
uint16_t imt_index_;
+ // Abstract class (non-interface) methods: Unused (zero-initialized).
};
// Fake padding field gets inserted here.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 73c90f58ac..57eb72dea5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3827,44 +3827,6 @@ class ClassLinker::OatClassCodeIterator {
const uint32_t num_methods_;
};
-inline void ClassLinker::LinkCode(ArtMethod* method,
- uint32_t class_def_method_index,
- /*inout*/ OatClassCodeIterator* occi) {
- ScopedAssertNoThreadSuspension sants(__FUNCTION__);
- Runtime* const runtime = Runtime::Current();
- if (runtime->IsAotCompiler()) {
- // The following code only applies to a non-compiler runtime.
- return;
- }
-
- // Method shouldn't have already been linked.
- DCHECK_EQ(method->GetEntryPointFromQuickCompiledCode(), nullptr);
- DCHECK(!method->GetDeclaringClass()->IsVisiblyInitialized()); // Actually ClassStatus::Idx.
-
- if (!method->IsInvokable()) {
- EnsureThrowsInvocationError(this, method);
- occi->SkipAbstract(class_def_method_index);
- return;
- }
-
- const void* quick_code = occi->GetAndAdvance(class_def_method_index);
- if (method->IsNative() && quick_code == nullptr) {
- const void* boot_jni_stub = FindBootJniStub(method);
- if (boot_jni_stub != nullptr) {
- // Use boot JNI stub if found.
- quick_code = boot_jni_stub;
- }
- }
- 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());
- }
-}
-
void ClassLinker::SetupClass(const DexFile& dex_file,
const dex::ClassDef& dex_class_def,
Handle<mirror::Class> klass,
@@ -3968,6 +3930,153 @@ class ClassLinker::MethodAnnotationsIterator {
const dex::MethodAnnotationsItem* const end_;
};
+void ClassLinker::LoadField(const ClassAccessor::Field& field,
+ Handle<mirror::Class> klass,
+ ArtField* dst) {
+ const uint32_t field_idx = field.GetIndex();
+ dst->SetDexFieldIndex(field_idx);
+ dst->SetDeclaringClass(klass.Get());
+
+ // Get access flags from the DexFile and set hiddenapi runtime access flags.
+ dst->SetAccessFlags(field.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(field));
+}
+
+void ClassLinker::LoadMethod(const DexFile& dex_file,
+ const ClassAccessor::Method& method,
+ ObjPtr<mirror::Class> klass,
+ /*inout*/ MethodAnnotationsIterator* mai,
+ /*out*/ ArtMethod* dst) {
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+
+ const uint32_t dex_method_idx = method.GetIndex();
+ const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
+ uint32_t name_utf16_length;
+ const char* method_name = dex_file.GetStringDataAndUtf16Length(method_id.name_idx_,
+ &name_utf16_length);
+ std::string_view shorty = dex_file.GetShortyView(dex_file.GetProtoId(method_id.proto_idx_));
+
+ dst->SetDexMethodIndex(dex_method_idx);
+ dst->SetDeclaringClass(klass);
+
+ // Get access flags from the DexFile and set hiddenapi runtime access flags.
+ uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method);
+
+ 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 the method has the right signature.
+ // When initializing without a boot image, `Object` and `Enum` shall have the finalizable
+ // flag cleared immediately after loading these classes, see `InitWithoutImage()`.
+ if (shorty == "V") {
+ klass->SetFinalizable();
+ }
+ } else if (method_name[0] == '<') {
+ // Fix broken access flags for initializers. Bug 11157540.
+ // `DexFileVerifier` rejects method names starting with '<' other than constructors.
+ DCHECK(has_ascii_name("<init>", sizeof("<init>") - 1u) ||
+ has_ascii_name("<clinit>", sizeof("<clinit>") - 1u)) << method_name;
+ if (UNLIKELY((access_flags & kAccConstructor) == 0)) {
+ LOG(WARNING) << method_name << " didn't have expected constructor access flag in class "
+ << klass->PrettyDescriptor() << " in dex file " << dex_file.GetLocation();
+ access_flags |= kAccConstructor;
+ }
+ }
+
+ access_flags |= GetNterpFastPathFlags(shorty, access_flags, kRuntimeQuickCodeISA);
+
+ if (UNLIKELY((access_flags & kAccNative) != 0u)) {
+ // Check if the native method is annotated with @FastNative or @CriticalNative.
+ const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx);
+ if (method_annotations != nullptr) {
+ access_flags |=
+ annotations::GetNativeMethodAnnotationAccessFlags(dex_file, *method_annotations);
+ }
+ dst->SetAccessFlags(access_flags);
+ DCHECK(!dst->IsAbstract());
+ DCHECK(!dst->HasCodeItem());
+ DCHECK_EQ(method.GetCodeItemOffset(), 0u);
+ 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::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx);
+ if (method_annotations != nullptr &&
+ annotations::MethodIsNeverCompile(dex_file, *method_annotations)) {
+ 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());
+ }
+ }
+
+ if ((access_flags & kAccAbstract) == 0u &&
+ Runtime::Current()->IsZygote() &&
+ !Runtime::Current()->GetJITOptions()->GetProfileSaverOptions().GetProfileBootClassPath()) {
+ DCHECK(!ArtMethod::IsAbstract(access_flags));
+ DCHECK(!ArtMethod::IsIntrinsic(access_flags));
+ dst->SetMemorySharedMethod();
+ dst->SetHotCounter();
+ }
+}
+
+inline void ClassLinker::LinkCode(ArtMethod* method,
+ uint32_t class_def_method_index,
+ /*inout*/ OatClassCodeIterator* occi) {
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+ Runtime* const runtime = Runtime::Current();
+ if (runtime->IsAotCompiler()) {
+ // The following code only applies to a non-compiler runtime.
+ return;
+ }
+
+ // Method shouldn't have already been linked.
+ DCHECK_EQ(method->GetEntryPointFromQuickCompiledCode(), nullptr);
+ DCHECK(!method->GetDeclaringClass()->IsVisiblyInitialized()); // Actually ClassStatus::Idx.
+
+ if (!method->IsInvokable()) {
+ EnsureThrowsInvocationError(this, method);
+ occi->SkipAbstract(class_def_method_index);
+ return;
+ }
+
+ const void* quick_code = occi->GetAndAdvance(class_def_method_index);
+ if (method->IsNative() && quick_code == nullptr) {
+ const void* boot_jni_stub = FindBootJniStub(method);
+ if (boot_jni_stub != nullptr) {
+ // Use boot JNI stub if found.
+ quick_code = boot_jni_stub;
+ }
+ }
+ 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());
+ }
+}
+
void ClassLinker::LoadClass(Thread* self,
const DexFile& dex_file,
const dex::ClassDef& dex_class_def,
@@ -4090,115 +4199,6 @@ void ClassLinker::LoadClass(Thread* self,
self->AllowThreadSuspension();
}
-void ClassLinker::LoadField(const ClassAccessor::Field& field,
- Handle<mirror::Class> klass,
- ArtField* dst) {
- const uint32_t field_idx = field.GetIndex();
- dst->SetDexFieldIndex(field_idx);
- dst->SetDeclaringClass(klass.Get());
-
- // Get access flags from the DexFile and set hiddenapi runtime access flags.
- dst->SetAccessFlags(field.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(field));
-}
-
-void ClassLinker::LoadMethod(const DexFile& dex_file,
- const ClassAccessor::Method& method,
- ObjPtr<mirror::Class> klass,
- /*inout*/ MethodAnnotationsIterator* mai,
- /*out*/ ArtMethod* dst) {
- ScopedAssertNoThreadSuspension sants(__FUNCTION__);
-
- const uint32_t dex_method_idx = method.GetIndex();
- const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
- uint32_t name_utf16_length;
- const char* method_name = dex_file.GetStringDataAndUtf16Length(method_id.name_idx_,
- &name_utf16_length);
- std::string_view shorty = dex_file.GetShortyView(dex_file.GetProtoId(method_id.proto_idx_));
-
- dst->SetDexMethodIndex(dex_method_idx);
- dst->SetDeclaringClass(klass);
-
- // Get access flags from the DexFile and set hiddenapi runtime access flags.
- uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method);
-
- 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 the method has the right signature.
- // When initializing without a boot image, `Object` and `Enum` shall have the finalizable
- // flag cleared immediately after loading these classes, see `InitWithoutImage()`.
- if (shorty == "V") {
- klass->SetFinalizable();
- }
- } else if (method_name[0] == '<') {
- // Fix broken access flags for initializers. Bug 11157540.
- // `DexFileVerifier` rejects method names starting with '<' other than constructors.
- DCHECK(has_ascii_name("<init>", sizeof("<init>") - 1u) ||
- has_ascii_name("<clinit>", sizeof("<clinit>") - 1u)) << method_name;
- if (UNLIKELY((access_flags & kAccConstructor) == 0)) {
- LOG(WARNING) << method_name << " didn't have expected constructor access flag in class "
- << klass->PrettyDescriptor() << " in dex file " << dex_file.GetLocation();
- access_flags |= kAccConstructor;
- }
- }
-
- access_flags |= GetNterpFastPathFlags(shorty, access_flags, kRuntimeQuickCodeISA);
-
- if (UNLIKELY((access_flags & kAccNative) != 0u)) {
- // Check if the native method is annotated with @FastNative or @CriticalNative.
- const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx);
- if (method_annotations != nullptr) {
- access_flags |=
- annotations::GetNativeMethodAnnotationAccessFlags(dex_file, *method_annotations);
- }
- dst->SetAccessFlags(access_flags);
- DCHECK(!dst->IsAbstract());
- DCHECK(!dst->HasCodeItem());
- DCHECK_EQ(method.GetCodeItemOffset(), 0u);
- 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::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx);
- if (method_annotations != nullptr &&
- annotations::MethodIsNeverCompile(dex_file, *method_annotations)) {
- 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());
- }
- }
-
- if ((access_flags & kAccAbstract) == 0u &&
- Runtime::Current()->IsZygote() &&
- !Runtime::Current()->GetJITOptions()->GetProfileSaverOptions().GetProfileBootClassPath()) {
- DCHECK(!ArtMethod::IsAbstract(access_flags));
- DCHECK(!ArtMethod::IsIntrinsic(access_flags));
- dst->SetMemorySharedMethod();
- dst->SetHotCounter();
- }
-}
-
void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile* dex_file) {
ObjPtr<mirror::DexCache> dex_cache =
AllocAndInitializeDexCache(self, *dex_file, /* class_loader= */ nullptr);
diff --git a/runtime/imtable-inl.h b/runtime/imtable-inl.h
index 2314df8fe4..8af1d89f60 100644
--- a/runtime/imtable-inl.h
+++ b/runtime/imtable-inl.h
@@ -33,59 +33,52 @@ static constexpr uint32_t kImTableHashCoefficientClass = 427;
static constexpr uint32_t kImTableHashCoefficientName = 16;
static constexpr uint32_t kImTableHashCoefficientSignature = 14;
-inline void ImTable::GetImtHashComponents(ArtMethod* method,
+inline void ImTable::GetImtHashComponents(const DexFile& dex_file,
+ uint32_t dex_method_index,
uint32_t* class_hash,
uint32_t* name_hash,
uint32_t* signature_hash) {
if (kImTableHashUseName) {
- const DexFile* dex_file = method->GetDexFile();
- const dex::MethodId& method_id = dex_file->GetMethodId(method->GetDexMethodIndex());
+ const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_index);
// Class descriptor for the class component.
- *class_hash = ComputeModifiedUtf8Hash(dex_file->GetMethodDeclaringClassDescriptor(method_id));
+ *class_hash = ComputeModifiedUtf8Hash(dex_file.GetMethodDeclaringClassDescriptor(method_id));
// Method name for the method component.
- *name_hash = ComputeModifiedUtf8Hash(dex_file->GetMethodName(method_id));
+ *name_hash = ComputeModifiedUtf8Hash(dex_file.GetMethodName(method_id));
- const dex::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
+ const dex::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id);
// Read the proto for the signature component.
uint32_t tmp = ComputeModifiedUtf8Hash(
- dex_file->GetTypeDescriptor(dex_file->GetTypeId(proto_id.return_type_idx_)));
+ dex_file.GetTypeDescriptor(dex_file.GetTypeId(proto_id.return_type_idx_)));
// Mix in the argument types.
// Note: we could consider just using the shorty. This would be faster, at the price of
// potential collisions.
- const dex::TypeList* param_types = dex_file->GetProtoParameters(proto_id);
+ const dex::TypeList* param_types = dex_file.GetProtoParameters(proto_id);
if (param_types != nullptr) {
for (size_t i = 0; i != param_types->Size(); ++i) {
const dex::TypeItem& type = param_types->GetTypeItem(i);
tmp = 31 * tmp + ComputeModifiedUtf8Hash(
- dex_file->GetTypeDescriptor(dex_file->GetTypeId(type.type_idx_)));
+ dex_file.GetTypeDescriptor(dex_file.GetTypeId(type.type_idx_)));
}
}
*signature_hash = tmp;
return;
} else {
- *class_hash = method->GetDexMethodIndex();
+ *class_hash = dex_method_index;
*name_hash = 0;
*signature_hash = 0;
return;
}
}
-inline uint32_t ImTable::GetImtIndex(ArtMethod* method) {
- DCHECK(!method->IsCopied());
- DCHECK(!method->IsProxyMethod());
- if (!method->IsAbstract()) {
- // For default methods, where we cannot store the imt_index, we use the
- // method_index instead. We mask it with the closest power of two to
- // simplify the interpreter.
- return method->GetMethodIndex() & (ImTable::kSizeTruncToPowerOfTwo - 1);
- }
+inline uint32_t ImTable::GetImtIndexForAbstractMethod(const DexFile& dex_file,
+ uint32_t dex_method_index) {
uint32_t class_hash, name_hash, signature_hash;
- GetImtHashComponents(method, &class_hash, &name_hash, &signature_hash);
+ GetImtHashComponents(dex_file, dex_method_index, &class_hash, &name_hash, &signature_hash);
uint32_t mixed_hash;
if (!kImTableHashUseCoefficients) {
@@ -99,6 +92,18 @@ inline uint32_t ImTable::GetImtIndex(ArtMethod* method) {
return mixed_hash % ImTable::kSize;
}
+inline uint32_t ImTable::GetImtIndex(ArtMethod* method) {
+ DCHECK(!method->IsCopied());
+ DCHECK(!method->IsProxyMethod());
+ if (!method->IsAbstract()) {
+ // For default methods, where we cannot store the imt_index, we use the
+ // method_index instead. We mask it with the closest power of two to
+ // simplify the interpreter.
+ return method->GetMethodIndex() & (ImTable::kSizeTruncToPowerOfTwo - 1);
+ }
+ return GetImtIndexForAbstractMethod(*method->GetDexFile(), method->GetDexMethodIndex());
+}
+
} // namespace art
#endif // ART_RUNTIME_IMTABLE_INL_H_
diff --git a/runtime/imtable.h b/runtime/imtable.h
index 0d604ca244..f7e9066c78 100644
--- a/runtime/imtable.h
+++ b/runtime/imtable.h
@@ -81,11 +81,14 @@ class ImTable {
}
// Converts a method to the base hash components used in GetImtIndex.
- ALWAYS_INLINE static inline void GetImtHashComponents(ArtMethod* method,
+ ALWAYS_INLINE static inline void GetImtHashComponents(const DexFile& dex_file,
+ uint32_t dex_method_index,
uint32_t* class_hash,
uint32_t* name_hash,
- uint32_t* signature_hash)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ uint32_t* signature_hash);
+
+ ALWAYS_INLINE static inline uint32_t GetImtIndexForAbstractMethod(const DexFile& dex_file,
+ uint32_t dex_method_index);
// The (complete) hashing scheme to map an ArtMethod to a slot in the Interface Method Table
// (IMT).