OSDN Git Service

Inline and optimize interface calls.
authorNicolas Geoffray <ngeoffray@google.com>
Wed, 6 Jul 2016 09:19:23 +0000 (10:19 +0100)
committerNicolas Geoffray <ngeoffray@google.com>
Wed, 6 Jul 2016 12:30:13 +0000 (13:30 +0100)
- Support for inlining methods with invoke-interface (the
  previous performance limitation is now fixed with the new
  ImtConflictTable).
- Turn non inlineable invoke-interface into invoke-virtual to
  avoid any potential execution of the conflict trampoline.

test:609-checker-inline-interface

Change-Id: Ibe33bc945deaada8fb78541e6e0bf034ebb52420

compiler/optimizing/inliner.cc
runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
test/478-checker-clinit-check-pruning/src/Main.java
test/548-checker-inlining-and-dce/src/Main.java
test/609-checker-inline-interface/expected.txt [new file with mode: 0644]
test/609-checker-inline-interface/info.txt [new file with mode: 0644]
test/609-checker-inline-interface/src/Main.java [new file with mode: 0644]

index c67b2d5..8ee04dd 100644 (file)
@@ -750,7 +750,35 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
 bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) {
   HInstruction* return_replacement = nullptr;
   if (!TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
-    return false;
+    if (invoke_instruction->IsInvokeInterface()) {
+      // Turn an invoke-interface into an invoke-virtual. An invoke-virtual is always
+      // better than an invoke-interface because:
+      // 1) In the best case, the interface call has one more indirection (to fetch the IMT).
+      // 2) We will not go to the conflict trampoline with an invoke-virtual.
+      // TODO: Consider sharpening once it is not dependent on the compiler driver.
+      HInvokeVirtual* new_invoke = new (graph_->GetArena()) HInvokeVirtual(
+          graph_->GetArena(),
+          invoke_instruction->GetNumberOfArguments(),
+          invoke_instruction->GetType(),
+          invoke_instruction->GetDexPc(),
+          invoke_instruction->GetDexMethodIndex(),
+          method->GetMethodIndex());
+      HInputsRef inputs = invoke_instruction->GetInputs();
+      size_t index = 0;
+      for (HInstruction* instr : inputs) {
+        new_invoke->SetArgumentAt(index++, instr);
+      }
+      invoke_instruction->GetBlock()->InsertInstructionBefore(new_invoke, invoke_instruction);
+      new_invoke->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+      if (invoke_instruction->GetType() == Primitive::kPrimNot) {
+        new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo());
+      }
+      return_replacement = new_invoke;
+    } else {
+      // TODO: Consider sharpening an invoke virtual once it is not dependent on the
+      // compiler driver.
+      return false;
+    }
   }
   if (return_replacement != nullptr) {
     invoke_instruction->ReplaceWith(return_replacement);
@@ -1239,14 +1267,6 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
         return false;
       }
 
-      if (current->IsInvokeInterface()) {
-        // Disable inlining of interface calls. The cost in case of entering the
-        // resolution conflict is currently too high.
-        VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
-                       << " could not be inlined because it has an interface call.";
-        return false;
-      }
-
       if (!same_dex_file && current->NeedsEnvironment()) {
         VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
                        << " could not be inlined because " << current->DebugName()
index 03771aa..923ea1a 100644 (file)
@@ -2141,11 +2141,7 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t deadbeef ATTRIBUT
   StackHandleScope<1> hs(self);
   Handle<mirror::Class> cls(hs.NewHandle(this_object->GetClass()));
 
-  // The optimizing compiler currently does not inline methods that have an interface
-  // invocation. We use the outer method directly to avoid fetching a stack map, which is
-  // more expensive.
-  ArtMethod* caller_method = QuickArgumentVisitor::GetOuterMethod(sp);
-  DCHECK_EQ(caller_method, QuickArgumentVisitor::GetCallingMethod(sp));
+  ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
 
   // Fetch the dex_method_idx of the target interface method from the caller.
   uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
index 6fc12f1..95ac1ee 100644 (file)
@@ -103,10 +103,8 @@ public class Main {
     static boolean doThrow = false;
 
     static void $noinline$staticMethod() {
-      if (doThrow) {
-        // Try defeating inlining.
-        throw new Error();
-      }
+      // Try defeating inlining.
+      if (doThrow) { throw new Error(); }
     }
   }
 
@@ -181,10 +179,8 @@ public class Main {
     static boolean doThrow = false;
 
     static void $noinline$staticMethod() {
-      if (doThrow) {
         // Try defeating inlining.
-        throw new Error();
-      }
+      if (doThrow) { throw new Error(); }
     }
   }
 
@@ -245,10 +241,8 @@ public class Main {
     static boolean doThrow = false;
 
     static void $noinline$staticMethod() {
-      if (doThrow) {
         // Try defeating inlining.
-        throw new Error();
-      }
+      if (doThrow) { throw new Error(); }
     }
 
     static {
@@ -314,7 +308,7 @@ public class Main {
 
   static void constClassAndInvokeStatic(Iterable<?> it) {
     $opt$inline$ignoreClass(ClassWithClinit7.class);
-    ClassWithClinit7.someStaticMethod(it);
+    ClassWithClinit7.$noinline$someStaticMethod(it);
   }
 
   static void $opt$inline$ignoreClass(Class<?> c) {
@@ -325,10 +319,10 @@ public class Main {
       System.out.println("Main$ClassWithClinit7's static initializer");
     }
 
-    // Note: not inlined from constClassAndInvokeStatic() but fully inlined from main().
-    static void someStaticMethod(Iterable<?> it) {
-      // We're not inlining invoke-interface at the moment.
+    static void $noinline$someStaticMethod(Iterable<?> it) {
       it.iterator();
+      // We're not inlining throw at the moment.
+      if (doThrow) { throw new Error(""); }
     }
   }
 
@@ -345,7 +339,7 @@ public class Main {
 
   static void sgetAndInvokeStatic(Iterable<?> it) {
     $opt$inline$ignoreInt(ClassWithClinit8.value);
-    ClassWithClinit8.someStaticMethod(it);
+    ClassWithClinit8.$noinline$someStaticMethod(it);
   }
 
   static void $opt$inline$ignoreInt(int i) {
@@ -357,10 +351,10 @@ public class Main {
       System.out.println("Main$ClassWithClinit8's static initializer");
     }
 
-    // Note: not inlined from sgetAndInvokeStatic() but fully inlined from main().
-    static void someStaticMethod(Iterable<?> it) {
-      // We're not inlining invoke-interface at the moment.
+    static void $noinline$someStaticMethod(Iterable<?> it) {
       it.iterator();
+      // We're not inlining throw at the moment.
+      if (doThrow) { throw new Error(""); }
     }
   }
 
@@ -377,7 +371,7 @@ public class Main {
   static void constClassSgetAndInvokeStatic(Iterable<?> it) {
     $opt$inline$ignoreClass(ClassWithClinit9.class);
     $opt$inline$ignoreInt(ClassWithClinit9.value);
-    ClassWithClinit9.someStaticMethod(it);
+    ClassWithClinit9.$noinline$someStaticMethod(it);
   }
 
   static class ClassWithClinit9 {
@@ -386,10 +380,10 @@ public class Main {
       System.out.println("Main$ClassWithClinit9's static initializer");
     }
 
-    // Note: not inlined from constClassSgetAndInvokeStatic() but fully inlined from main().
-    static void someStaticMethod(Iterable<?> it) {
-      // We're not inlining invoke-interface at the moment.
+    static void $noinline$someStaticMethod(Iterable<?> it) {
       it.iterator();
+      // We're not inlining throw at the moment.
+      if (doThrow) { throw new Error(""); }
     }
   }
 
@@ -422,8 +416,9 @@ public class Main {
 
     static void inlinedForNull(Iterable<?> it) {
       if (it != null) {
-        // We're not inlining invoke-interface at the moment.
         it.iterator();
+        // We're not inlining throw at the moment.
+        if (doThrow) { throw new Error(""); }
       }
     }
   }
@@ -460,8 +455,11 @@ public class Main {
     }
 
     static void inlinedForNull(Iterable<?> it) {
-      // We're not inlining invoke-interface at the moment.
       it.iterator();
+      if (it != null) {
+        // We're not inlining throw at the moment.
+        if (doThrow) { throw new Error(""); }
+      }
     }
   }
 
@@ -494,8 +492,8 @@ public class Main {
 
     static void inlinedForNull(Iterable<?> it) {
       if (it != null) {
-        // We're not inlining invoke-interface at the moment.
-        it.iterator();
+        // We're not inlining throw at the moment.
+        if (doThrow) { throw new Error(""); }
       }
     }
   }
@@ -510,8 +508,9 @@ public class Main {
     }
 
     public static void $noinline$getIterator(Iterable<?> it) {
-      // We're not inlining invoke-interface at the moment.
       it.iterator();
+      // We're not inlining throws at the moment.
+      if (doThrow) { throw new Error(""); }
     }
   }
 
index 38fdcc0..bf64c3b 100644 (file)
 
 public class Main {
 
+  static boolean doThrow = false;
+
   private void inlinedForNull(Iterable it) {
     if (it != null) {
-      // We're not inlining invoke-interface at the moment.
-      it.iterator();
+      // We're not inlining throw at the moment.
+      if (doThrow) { throw new Error(""); }
     }
   }
 
   private void inlinedForFalse(boolean value, Iterable it) {
     if (value) {
-      // We're not inlining invoke-interface at the moment.
-      it.iterator();
+      // We're not inlining throw at the moment.
+      if (doThrow) { throw new Error(""); }
     }
   }
 
diff --git a/test/609-checker-inline-interface/expected.txt b/test/609-checker-inline-interface/expected.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/609-checker-inline-interface/info.txt b/test/609-checker-inline-interface/info.txt
new file mode 100644 (file)
index 0000000..35eee08
--- /dev/null
@@ -0,0 +1,2 @@
+Checker test that we inline interface calls and if we can't inline
+them, we can turn them into a virtual invoke.
diff --git a/test/609-checker-inline-interface/src/Main.java b/test/609-checker-inline-interface/src/Main.java
new file mode 100644 (file)
index 0000000..e9b3e87
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public final class Main implements Interface {
+
+  static void methodWithInvokeInterface(Interface interf) {
+    interf.$noinline$doCall();
+  }
+
+  public void $noinline$doCall() {
+    if (doThrow) throw new Error("");
+  }
+
+  public static void main(String[] args) {
+    testInlineInterfaceCall();
+    testInterfaceToVirtualCall();
+  }
+
+  /// CHECK-START: void Main.testInlineInterfaceCall() inliner (before)
+  /// CHECK:                          InvokeStaticOrDirect method_name:Main.methodWithInvokeInterface
+
+  /// CHECK-START: void Main.testInlineInterfaceCall() inliner (before)
+  /// CHECK-NOT:                      InvokeInterface
+
+  /// CHECK-START: void Main.testInlineInterfaceCall() inliner (after)
+  /// CHECK:                          InvokeInterface method_name:Interface.$noinline$doCall
+
+  /// CHECK-START: void Main.testInlineInterfaceCall() inliner (after)
+  /// CHECK-NOT:                      InvokeStaticOrDirect
+  public static void testInlineInterfaceCall() {
+    methodWithInvokeInterface(itf);
+  }
+
+  /// CHECK-START: void Main.testInterfaceToVirtualCall() inliner (before)
+  /// CHECK:                          InvokeStaticOrDirect method_name:Main.methodWithInvokeInterface
+
+  /// CHECK-START: void Main.testInterfaceToVirtualCall() inliner (before)
+  /// CHECK-NOT:                      InvokeInterface
+
+  /// CHECK-START: void Main.testInterfaceToVirtualCall() inliner (after)
+  /// CHECK:                          InvokeVirtual method_name:Main.$noinline$doCall
+
+  /// CHECK-START: void Main.testInterfaceToVirtualCall() inliner (after)
+  /// CHECK-NOT:                      InvokeStaticOrDirect
+  /// CHECK-NOT:                      InvokeInterface
+  public static void testInterfaceToVirtualCall() {
+    methodWithInvokeInterface(m);
+  }
+
+  static Interface itf = new Main();
+  static Main m = new Main();
+  static boolean doThrow = false;
+}
+
+interface Interface {
+  public void $noinline$doCall();
+}