diff options
author | 2025-02-26 18:19:29 +0000 | |
---|---|---|
committer | 2025-02-27 10:51:40 -0800 | |
commit | 12f7d1eb0ff3fe0126d8dadd6bbfa8b797718e9c (patch) | |
tree | 02c4ac57b6a3ab39c2a36ac8f031e1562751859d | |
parent | 6d9c6c00c78afb2e5d37f8c1b47b0a4e8772356e (diff) |
Fast field lookup in nterp.
Only increase the hotness if we fail the fast lookup.
Test: test.py
Change-Id: I4526181eda83b3648383788738deaf71418de825
-rw-r--r-- | runtime/interpreter/mterp/nterp.cc | 141 | ||||
-rw-r--r-- | runtime/mirror/class-inl.h | 32 | ||||
-rw-r--r-- | runtime/mirror/class.cc | 30 | ||||
-rw-r--r-- | runtime/mirror/class.h | 3 |
4 files changed, 128 insertions, 78 deletions
diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc index fc6168c70d..8ce7e42a84 100644 --- a/runtime/interpreter/mterp/nterp.cc +++ b/runtime/interpreter/mterp/nterp.cc @@ -391,33 +391,66 @@ extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, const uint16_t } } +ALWAYS_INLINE FLATTEN +static ArtField* FindFieldFast(ArtMethod* caller, uint16_t field_index) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (caller->IsObsolete()) { + return nullptr; + } + + const dex::FieldId& field_id = caller->GetDexFile()->GetFieldId(field_index); + ObjPtr<mirror::Class> cls = caller->GetDeclaringClass(); + if (cls->GetDexTypeIndex() == field_id.class_idx_) { + // Field is in the same class as the caller, no need to do access checks. + return cls->FindDeclaredField(field_index); + } + + return nullptr; +} + +NO_INLINE +static ArtField* FindFieldSlow(Thread* self, + ArtMethod* caller, + uint16_t field_index, + bool is_static, + bool is_put) + REQUIRES_SHARED(Locks::mutator_lock_) { + return ResolveFieldWithAccessChecks( + self, + Runtime::Current()->GetClassLinker(), + field_index, + caller, + is_static, + /*is_put=*/ is_put, + /*resolve_field_type=*/ 0); +} + LIBART_PROTECTED extern "C" size_t NterpGetStaticField(Thread* self, ArtMethod* caller, const uint16_t* dex_pc_ptr, size_t resolve_field_type) // Resolve if not zero REQUIRES_SHARED(Locks::mutator_lock_) { - UpdateHotness(caller); const Instruction* inst = Instruction::At(dex_pc_ptr); uint16_t field_index = inst->VRegB_21c(); - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); Instruction::Code opcode = inst->Opcode(); - ArtField* resolved_field = ResolveFieldWithAccessChecks( - self, - class_linker, - field_index, - caller, - /*is_static=*/ true, - /*is_put=*/ IsInstructionSPut(opcode), - resolve_field_type); - if (resolved_field == nullptr) { - DCHECK(self->IsExceptionPending()); - return 0; + ArtField* resolved_field = FindFieldFast(caller, field_index); + if (resolved_field == nullptr || !resolved_field->IsStatic()) { + resolved_field = FindFieldSlow( + self, caller, field_index, /*is_static=*/ true, IsInstructionSPut(opcode)); + if (resolved_field == nullptr) { + DCHECK(self->IsExceptionPending()); + return 0; + } + // Only update hotness for slow lookups. + UpdateHotness(caller); } + if (UNLIKELY(!resolved_field->GetDeclaringClass()->IsVisiblyInitialized())) { StackHandleScope<1> hs(self); Handle<mirror::Class> h_class(hs.NewHandle(resolved_field->GetDeclaringClass())); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); if (UNLIKELY(!class_linker->EnsureInitialized( self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true))) { DCHECK(self->IsExceptionPending()); @@ -425,26 +458,32 @@ extern "C" size_t NterpGetStaticField(Thread* self, } DCHECK(h_class->IsInitializing()); } + + // For sput-object, try to resolve the field type even if we were not requested to. + // Only if the field type is successfully resolved can we update the cache. If we + // fail to resolve the type, we clear the exception to keep interpreter + // semantics of not throwing when null is stored. + bool update_cache = true; + if (opcode == Instruction::SPUT_OBJECT && resolved_field->ResolveType() == nullptr) { + DCHECK(self->IsExceptionPending()); + if (resolve_field_type) { + return 0; + } + self->ClearException(); + update_cache = false; + } + if (resolved_field->IsVolatile()) { // Or the result with 1 to notify to nterp this is a volatile field. We // also don't cache the result as we don't want nterp to have its fast path always // check for it. return reinterpret_cast<size_t>(resolved_field) | 1; - } else { - // For sput-object, try to resolve the field type even if we were not requested to. - // Only if the field type is successfully resolved can we update the cache. If we - // fail to resolve the type, we clear the exception to keep interpreter - // semantics of not throwing when null is stored. - if (opcode == Instruction::SPUT_OBJECT && - resolve_field_type == 0 && - resolved_field->ResolveType() == nullptr) { - DCHECK(self->IsExceptionPending()); - self->ClearException(); - } else { - UpdateCache(self, dex_pc_ptr, resolved_field); - } - return reinterpret_cast<size_t>(resolved_field); } + + if (update_cache) { + UpdateCache(self, dex_pc_ptr, resolved_field); + } + return reinterpret_cast<size_t>(resolved_field); } LIBART_PROTECTED @@ -453,38 +492,42 @@ extern "C" uint32_t NterpGetInstanceFieldOffset(Thread* self, const uint16_t* dex_pc_ptr, size_t resolve_field_type) // Resolve if not zero REQUIRES_SHARED(Locks::mutator_lock_) { - UpdateHotness(caller); const Instruction* inst = Instruction::At(dex_pc_ptr); uint16_t field_index = inst->VRegC_22c(); - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); Instruction::Code opcode = inst->Opcode(); - ArtField* resolved_field = ResolveFieldWithAccessChecks( - self, - class_linker, - field_index, - caller, - /*is_static=*/ false, - /*is_put=*/ IsInstructionIPut(opcode), - resolve_field_type); - if (resolved_field == nullptr) { - DCHECK(self->IsExceptionPending()); - return 0; - } - if (resolved_field->IsVolatile()) { - // Don't cache for a volatile field, and return a negative offset as marker - // of volatile. - return -resolved_field->GetOffset().Uint32Value(); + + ArtField* resolved_field = FindFieldFast(caller, field_index); + if (resolved_field == nullptr || resolved_field->IsStatic()) { + resolved_field = FindFieldSlow( + self, caller, field_index, /*is_static=*/ false, IsInstructionIPut(opcode)); + if (resolved_field == nullptr) { + DCHECK(self->IsExceptionPending()); + return 0; + } + // Only update hotness for slow lookups. + UpdateHotness(caller); } + // For iput-object, try to resolve the field type even if we were not requested to. // Only if the field type is successfully resolved can we update the cache. If we // fail to resolve the type, we clear the exception to keep interpreter // semantics of not throwing when null is stored. - if (opcode == Instruction::IPUT_OBJECT && - resolve_field_type == 0 && - resolved_field->ResolveType() == nullptr) { + bool update_cache = true; + if (opcode == Instruction::IPUT_OBJECT && resolved_field->ResolveType() == nullptr) { DCHECK(self->IsExceptionPending()); + if (resolve_field_type != 0u) { + return 0; + } self->ClearException(); - } else { + update_cache = false; + } + + if (resolved_field->IsVolatile()) { + // Don't cache for a volatile field, and return a negative offset as marker + // of volatile. + return -resolved_field->GetOffset().Uint32Value(); + } + if (update_cache) { UpdateCache(self, dex_pc_ptr, resolved_field->GetOffset().Uint32Value()); } return resolved_field->GetOffset().Uint32Value(); diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 40f119e3c4..373b9b3105 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -1356,6 +1356,38 @@ inline ImTable* Class::FindSuperImt(PointerSize pointer_size) { return nullptr; } +ALWAYS_INLINE FLATTEN inline ArtField* Class::FindDeclaredField(uint32_t dex_field_idx) { + size_t num_fields = NumFields(); + if (num_fields > 0) { + // The field array is an ordered list of fields where there may be missing + // indices. For example, it could be [40, 42], but in 90% of cases cases we have + // [40, 41, 42]. The latter is the case we are optimizing for, where for + // example `dex_field_idx` is 41, and we can just substract it with the + // first field index (40) and directly access the array with that index (1). + uint32_t index = dex_field_idx - GetField(0)->GetDexFieldIndex(); + if (index < num_fields) { + ArtField* field = GetField(index); + if (field->GetDexFieldIndex() == dex_field_idx) { + return field; + } + } else { + index = num_fields; + } + // If there is a field, it's down the array. The array is ordered by field + // index, so we know we can stop the search if `dex_field_idx` is greater + // than the current field's index. + for (; index > 0; --index) { + ArtField* field = GetField(index - 1); + if (field->GetDexFieldIndex() == dex_field_idx) { + return field; + } else if (field->GetDexFieldIndex() < dex_field_idx) { + break; + } + } + } + return nullptr; +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 0bf1b1cc0a..01f8fb3201 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -1173,35 +1173,7 @@ ArtField* Class::FindDeclaredStaticField(std::string_view name, std::string_view } ArtField* Class::FindDeclaredField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx) { - size_t num_fields = NumFields(); - if (dex_cache == GetDexCache() && num_fields > 0) { - // The field array is an ordered list of fields where there may be missing - // indices. For example, it could be [40, 42], but in 90% of cases cases we have - // [40, 41, 42]. The latter is the case we are optimizing for, where for - // example `dex_field_idx` is 41, and we can just substract it with the - // first field index (40) and directly access the array with that index (1). - uint32_t index = dex_field_idx - GetField(0)->GetDexFieldIndex(); - if (index < num_fields) { - ArtField* field = GetField(index); - if (field->GetDexFieldIndex() == dex_field_idx) { - return field; - } - } else { - index = num_fields; - } - // If there is a field, it's down the array. The array is ordered by field - // index, so we know we can stop the search if `dex_field_idx` is greater - // than the current field's index. - for (; index > 0; --index) { - ArtField* field = GetField(index - 1); - if (field->GetDexFieldIndex() == dex_field_idx) { - return field; - } else if (field->GetDexFieldIndex() < dex_field_idx) { - break; - } - } - } - return nullptr; + return (dex_cache == GetDexCache()) ? FindDeclaredField(dex_field_idx) : nullptr; } ArtField* Class::FindDeclaredStaticField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx) { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 9a45a73e72..6e72a41592 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -1123,6 +1123,9 @@ class EXPORT MANAGED Class final : public Object { ArtField* FindDeclaredField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx) REQUIRES_SHARED(Locks::mutator_lock_); + ArtField* FindDeclaredField(uint32_t dex_field_idx) + REQUIRES_SHARED(Locks::mutator_lock_); + ArtField* FindDeclaredField(std::string_view name, std::string_view type) REQUIRES_SHARED(Locks::mutator_lock_); |