Optimizing compiler support for directly calling interface methods
This teaches the optimizing compiler how to perform invoke-super on
interfaces. This should make the invokes generally faster.
Bug: 24618811
Change-Id: I7f9b0fb1209775c1c8837ab5d21f8acba3cc72a5
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index ea1afa8..7e8a4a4 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -116,6 +116,30 @@
return resolved_method;
}
+inline mirror::Class* ClassLinker::ResolveReferencedClassOfMethod(Thread* self,
+ uint32_t method_idx,
+ ArtMethod* referrer) {
+ // NB: We cannot simply use `GetResolvedMethod(method_idx, ...)->GetDeclaringClass()`. This is
+ // because if we did so than an invoke-super could be incorrectly dispatched in cases where
+ // GetMethodId(method_idx).class_idx_ refers to a non-interface, non-direct-superclass
+ // (super*-class?) of the referrer and the direct superclass of the referrer contains a concrete
+ // implementation of the method. If this class's implementation of the method is copied from an
+ // interface (either miranda, default or conflict) we would incorrectly assume that is what we
+ // want to invoke on, instead of the 'concrete' implementation that the direct superclass
+ // contains.
+ mirror::Class* declaring_class = referrer->GetDeclaringClass();
+ StackHandleScope<2> hs(self);
+ Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+ const DexFile* dex_file = h_dex_cache->GetDexFile();
+ const DexFile::MethodId& method = dex_file->GetMethodId(method_idx);
+ mirror::Class* resolved_type = h_dex_cache->GetResolvedType(method.class_idx_);
+ if (UNLIKELY(resolved_type == nullptr)) {
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
+ resolved_type = ResolveType(*dex_file, method.class_idx_, h_dex_cache, class_loader);
+ }
+ return resolved_type;
+}
+
template <ClassLinker::ResolveMode kResolveMode>
inline ArtMethod* ClassLinker::ResolveMethod(Thread* self,
uint32_t method_idx,
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 4975c29..99dd073 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -284,6 +284,15 @@
ArtMethod* GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer)
SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // This returns the class referred to by GetMethodId(method_idx).class_idx_. This might be
+ // different then the declaring class of the resolved method due to copied
+ // miranda/default/conflict methods.
+ mirror::Class* ResolveReferencedClassOfMethod(Thread* self,
+ uint32_t method_idx,
+ ArtMethod* referrer)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
template <ResolveMode kResolveMode>
ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type)
SHARED_REQUIRES(Locks::mutator_lock_)
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 638fdb4..24986253 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1036,8 +1036,15 @@
} else {
DCHECK_EQ(invoke_type, kSuper);
CHECK(caller != nullptr) << invoke_type;
- called = caller->GetDeclaringClass()->GetSuperClass()->GetVTableEntry(
- called->GetMethodIndex(), sizeof(void*));
+ // TODO Maybe put this into a mirror::Class function.
+ mirror::Class* ref_class = linker->ResolveReferencedClassOfMethod(
+ self, called_method.dex_method_index, caller);
+ if (ref_class->IsInterface()) {
+ called = ref_class->FindVirtualMethodForInterfaceSuper(called, sizeof(void*));
+ } else {
+ called = caller->GetDeclaringClass()->GetSuperClass()->GetVTableEntry(
+ called->GetMethodIndex(), sizeof(void*));
+ }
}
CHECK(called != nullptr) << PrettyMethod(orig_called) << " "
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 2890a98..5f40123 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3948,11 +3948,27 @@
// If we're using invoke-super(method), make sure that the executing method's class' superclass
// has a vtable entry for the target method. Or the target is on a interface.
if (method_type == METHOD_SUPER) {
- if (res_method->GetDeclaringClass()->IsInterface()) {
- // TODO Fill in this part. Verify what we can...
- if (Runtime::Current()->IsAotCompiler()) {
- Fail(VERIFY_ERROR_FORCE_INTERPRETER) << "Currently we only allow invoke-super in "
- << "interpreter when using interface methods";
+ uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_;
+ mirror::Class* reference_class = dex_cache_->GetResolvedType(class_idx);
+ if (reference_class == nullptr) {
+ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Unable to find referenced class from invoke-super";
+ return nullptr;
+ }
+ if (reference_class->IsInterface()) {
+ // TODO Can we verify anything else.
+ if (class_idx == class_def_->class_idx_) {
+ Fail(VERIFY_ERROR_CLASS_CHANGE) << "Cannot invoke-super on self as interface";
+ }
+ // TODO Revisit whether we want to allow invoke-super on direct interfaces only like the JLS
+ // does.
+ mirror::Class* this_class = GetDeclaringClass().GetClass();
+ if (!reference_class->IsAssignableFrom(this_class)) {
+ Fail(VERIFY_ERROR_CLASS_CHANGE)
+ << "invoke-super in " << PrettyClass(this_class) << " in method "
+ << PrettyMethod(dex_method_idx_, *dex_file_) << " to method "
+ << PrettyMethod(method_idx, *dex_file_) << " references "
+ << "non-super-interface type " << PrettyClass(reference_class);
+ return nullptr;
}
} else {
const RegType& super = GetDeclaringClass().GetSuperClass(®_types_);