Use the small thread-local cache for mterp invokes.
This speeds up non-quickened interpreter by 2% (measured on golem).
Test: ./art/test.py -b -r --interpreter --host
Change-Id: I6b00db1b2da7fda4cb0a34beb62d3857ae3d72df
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 1045d2a..4c52ed3 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -420,28 +420,17 @@
#undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL
#undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL
+// Follow virtual/interface indirections if applicable.
+// Will throw null-pointer exception the if the object is null.
template<InvokeType type, bool access_check>
-inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
- ObjPtr<mirror::Object>* this_object,
- ArtMethod* referrer,
- Thread* self) {
+ALWAYS_INLINE ArtMethod* FindMethodToCall(uint32_t method_idx,
+ ArtMethod* resolved_method,
+ ObjPtr<mirror::Object>* this_object,
+ ArtMethod* referrer,
+ Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- constexpr ClassLinker::ResolveMode resolve_mode =
- access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE
- : ClassLinker::ResolveMode::kNoChecks;
- ArtMethod* resolved_method;
- if (type == kStatic) {
- resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
- } else {
- StackHandleScope<1> hs(self);
- HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object));
- resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
- }
- if (UNLIKELY(resolved_method == nullptr)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- }
- // Next, null pointer check.
+ // Null pointer check.
if (UNLIKELY(*this_object == nullptr && type != kStatic)) {
if (UNLIKELY(resolved_method->GetDeclaringClass()->IsStringClass() &&
resolved_method->IsConstructor())) {
@@ -570,6 +559,31 @@
}
}
+template<InvokeType type, bool access_check>
+inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
+ ObjPtr<mirror::Object>* this_object,
+ ArtMethod* referrer,
+ Thread* self) {
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ constexpr ClassLinker::ResolveMode resolve_mode =
+ access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE
+ : ClassLinker::ResolveMode::kNoChecks;
+ ArtMethod* resolved_method;
+ if (type == kStatic) {
+ resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
+ } else {
+ StackHandleScope<1> hs(self);
+ HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object));
+ resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
+ }
+ if (UNLIKELY(resolved_method == nullptr)) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
+ return nullptr; // Failure.
+ }
+ return FindMethodToCall<type, access_check>(
+ method_idx, resolved_method, this_object, referrer, self);
+}
+
// Explicit template declarations of FindMethodFromCode for all invoke types.
#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
template REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE \
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index a607b48..a598b8f 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -136,11 +136,34 @@
}
const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+ ArtMethod* sf_method = shadow_frame.GetMethod();
+
+ // Try to find the method in small thread-local cache first.
+ InterpreterCache* tls_cache = self->GetInterpreterCache();
+ size_t tls_value;
+ ArtMethod* resolved_method;
+ if (LIKELY(tls_cache->Get(inst, &tls_value))) {
+ resolved_method = reinterpret_cast<ArtMethod*>(tls_value);
+ } else {
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ constexpr ClassLinker::ResolveMode resolve_mode =
+ do_access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE
+ : ClassLinker::ResolveMode::kNoChecks;
+ resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, sf_method, type);
+ if (UNLIKELY(resolved_method == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ result->SetJ(0);
+ return false;
+ }
+ tls_cache->Set(inst, reinterpret_cast<size_t>(resolved_method));
+ }
+
+ // Null pointer check and virtual method resolution.
ObjPtr<mirror::Object> receiver =
(type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC);
- ArtMethod* sf_method = shadow_frame.GetMethod();
- ArtMethod* const called_method = FindMethodFromCode<type, do_access_check>(
- method_idx, &receiver, sf_method, self);
+ ArtMethod* const called_method = FindMethodToCall<type, do_access_check>(
+ method_idx, resolved_method, &receiver, sf_method, self);
+
// The shadow frame should already be pushed, so we don't need to update it.
if (UNLIKELY(called_method == nullptr)) {
CHECK(self->IsExceptionPending());