summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nicolas Geoffray <ngeoffray@google.com> 2025-02-26 18:19:29 +0000
committer Nicolas Geoffray <ngeoffray@google.com> 2025-02-27 10:51:40 -0800
commit12f7d1eb0ff3fe0126d8dadd6bbfa8b797718e9c (patch)
tree02c4ac57b6a3ab39c2a36ac8f031e1562751859d
parent6d9c6c00c78afb2e5d37f8c1b47b0a4e8772356e (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.cc141
-rw-r--r--runtime/mirror/class-inl.h32
-rw-r--r--runtime/mirror/class.cc30
-rw-r--r--runtime/mirror/class.h3
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_);