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/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 0eb3e43..0d65bc7 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -305,11 +305,31 @@
     MethodReference* target_method, const MethodReference* devirt_target,
     uintptr_t* direct_code, uintptr_t* direct_method) {
   // Don't try to fast-path if we don't understand the caller's class.
+  // Referrer_class is the class that this invoke is contained in.
   if (UNLIKELY(referrer_class == nullptr)) {
     return 0;
   }
-  mirror::Class* methods_class = resolved_method->GetDeclaringClass();
-  if (UNLIKELY(!referrer_class->CanAccessResolvedMethod(methods_class, resolved_method,
+  StackHandleScope<2> hs(soa.Self());
+  // Methods_class is the class refered to by the class_idx field of the methodId the method_idx is
+  // pointing to.
+  // For example in
+  //   .class LABC;
+  //   .super LDEF;
+  //   .method hi()V
+  //     ...
+  //     invoke-super {p0}, LDEF;->hi()V
+  //     ...
+  //   .end method
+  // the referrer_class is 'ABC' and the methods_class is DEF. Note that the methods class is 'DEF'
+  // even if 'DEF' inherits the method from it's superclass.
+  Handle<mirror::Class> methods_class(hs.NewHandle(mUnit->GetClassLinker()->ResolveType(
+      *target_method->dex_file,
+      target_method->dex_file->GetMethodId(target_method->dex_method_index).class_idx_,
+      dex_cache,
+      class_loader)));
+  DCHECK(methods_class.Get() != nullptr);
+  mirror::Class* methods_declaring_class = resolved_method->GetDeclaringClass();
+  if (UNLIKELY(!referrer_class->CanAccessResolvedMethod(methods_declaring_class, resolved_method,
                                                         dex_cache.Get(),
                                                         target_method->dex_method_index))) {
     return 0;
@@ -318,18 +338,31 @@
   // overridden (ie is final).
   const bool same_dex_file = target_method->dex_file == mUnit->GetDexFile();
   bool can_sharpen_virtual_based_on_type = same_dex_file &&
-      (*invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal());
+      (*invoke_type == kVirtual) && (resolved_method->IsFinal() ||
+                                     methods_declaring_class->IsFinal());
   // For invoke-super, ensure the vtable index will be correct to dispatch in the vtable of
   // the super class.
   const size_t pointer_size = InstructionSetPointerSize(GetInstructionSet());
-  bool can_sharpen_super_based_on_type = same_dex_file && (*invoke_type == kSuper) &&
-      (referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) &&
-      resolved_method->GetMethodIndex() < methods_class->GetVTableLength() &&
-      (methods_class->GetVTableEntry(
+  // TODO We should be able to sharpen if we are going into the boot image as well.
+  bool can_sharpen_super_based_on_type = same_dex_file &&
+      (*invoke_type == kSuper) &&
+      !methods_class->IsInterface() &&
+      (referrer_class != methods_declaring_class) &&
+      referrer_class->IsSubClass(methods_declaring_class) &&
+      resolved_method->GetMethodIndex() < methods_declaring_class->GetVTableLength() &&
+      (methods_declaring_class->GetVTableEntry(
           resolved_method->GetMethodIndex(), pointer_size) == resolved_method) &&
       resolved_method->IsInvokable();
+  // TODO We should be able to sharpen if we are going into the boot image as well.
+  bool can_sharpen_interface_super_based_on_type = same_dex_file &&
+      (*invoke_type == kSuper) &&
+      methods_class->IsInterface() &&
+      methods_class->IsAssignableFrom(referrer_class) &&
+      resolved_method->IsInvokable();
 
-  if (can_sharpen_virtual_based_on_type || can_sharpen_super_based_on_type) {
+  if (can_sharpen_virtual_based_on_type ||
+      can_sharpen_super_based_on_type ||
+      can_sharpen_interface_super_based_on_type) {
     // Sharpen a virtual call into a direct call. The method_idx is into referrer's
     // dex cache, check that this resolved method is where we expect it.
     CHECK_EQ(target_method->dex_file, mUnit->GetDexFile());
@@ -363,7 +396,6 @@
           *devirt_target->dex_file, devirt_target->dex_method_index, dex_cache, class_loader,
           nullptr, kVirtual);
     } else {
-      StackHandleScope<1> hs(soa.Self());
       auto target_dex_cache(hs.NewHandle(class_linker->RegisterDexFile(
           *devirt_target->dex_file,
           class_linker->GetOrCreateAllocatorForClassLoader(class_loader.Get()))));
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 3721813..c7430e7 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -798,7 +798,7 @@
 
 ArtMethod* HGraphBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<2> hs(soa.Self());
+  StackHandleScope<3> hs(soa.Self());
 
   ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
@@ -833,31 +833,56 @@
   }
 
   // We have to special case the invoke-super case, as ClassLinker::ResolveMethod does not.
-  // We need to look at the referrer's super class vtable.
+  // We need to look at the referrer's super class vtable. We need to do this to know if we need to
+  // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of
+  // which require runtime handling.
   if (invoke_type == kSuper) {
     if (compiling_class.Get() == nullptr) {
-      // Invoking a super method requires knowing the actual super class. If we did not resolve
-      // the compiling method's declaring class (which only happens for ahead of time compilation),
-      // bail out.
+      // We could not determine the method's class we need to wait until runtime.
       DCHECK(Runtime::Current()->IsAotCompiler());
       return nullptr;
     }
-    uint16_t vtable_index = resolved_method->GetMethodIndex();
-    ArtMethod* actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
-        vtable_index, class_linker->GetImagePointerSize());
-    if (actual_method != resolved_method &&
-        !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
-      // TODO: The actual method could still be referenced in the current dex file, so we
-      // could try locating it.
-      // TODO: Remove the dex_file restriction.
+    ArtMethod* current_method = graph_->GetArtMethod();
+    DCHECK(current_method != nullptr);
+    Handle<mirror::Class> methods_class(hs.NewHandle(
+        dex_compilation_unit_->GetClassLinker()->ResolveReferencedClassOfMethod(Thread::Current(),
+                                                                                method_idx,
+                                                                                current_method)));
+    if (methods_class.Get() == nullptr) {
+      // Invoking a super method requires knowing the actual super class. If we did not resolve
+      // the compiling method's declaring class (which only happens for ahead of time
+      // compilation), bail out.
+      DCHECK(Runtime::Current()->IsAotCompiler());
       return nullptr;
+    } else {
+      ArtMethod* actual_method;
+      if (methods_class->IsInterface()) {
+        actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
+            resolved_method, class_linker->GetImagePointerSize());
+      } else {
+        uint16_t vtable_index = resolved_method->GetMethodIndex();
+        actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
+            vtable_index, class_linker->GetImagePointerSize());
+      }
+      if (actual_method != resolved_method &&
+          !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
+        // The back-end code generator relies on this check in order to ensure that it will not
+        // attempt to read the dex_cache with a dex_method_index that is not from the correct
+        // dex_file. If we didn't do this check then the dex_method_index will not be updated in the
+        // builder, which means that the code-generator (and compiler driver during sharpening and
+        // inliner, maybe) might invoke an incorrect method.
+        // TODO: The actual method could still be referenced in the current dex file, so we
+        //       could try locating it.
+        // TODO: Remove the dex_file restriction.
+        return nullptr;
+      }
+      if (!actual_method->IsInvokable()) {
+        // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
+        // could resolve the callee to the wrong method.
+        return nullptr;
+      }
+      resolved_method = actual_method;
     }
-    if (!actual_method->IsInvokable()) {
-      // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
-      // could resolve the callee to the wrong method.
-      return nullptr;
-    }
-    resolved_method = actual_method;
   }
 
   // Check for incompatible class changes. The class linker has a fast path for
@@ -923,7 +948,7 @@
 
   ArtMethod* resolved_method = ResolveMethod(method_idx, invoke_type);
 
-  if (resolved_method == nullptr) {
+  if (UNLIKELY(resolved_method == nullptr)) {
     MaybeRecordStat(MethodCompilationStat::kUnresolvedMethod);
     HInvoke* invoke = new (arena_) HInvokeUnresolved(arena_,
                                                      number_of_arguments,
@@ -943,7 +968,6 @@
   // Potential class initialization check, in the case of a static method call.
   HClinitCheck* clinit_check = nullptr;
   HInvoke* invoke = nullptr;
-
   if (invoke_type == kDirect || invoke_type == kStatic || invoke_type == kSuper) {
     // By default, consider that the called method implicitly requires
     // an initialization check of its declaring method.