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);
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()
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);
static boolean doThrow = false;
static void $noinline$staticMethod() {
- if (doThrow) {
- // Try defeating inlining.
- throw new Error();
- }
+ // Try defeating inlining.
+ if (doThrow) { throw new Error(); }
}
}
static boolean doThrow = false;
static void $noinline$staticMethod() {
- if (doThrow) {
// Try defeating inlining.
- throw new Error();
- }
+ if (doThrow) { throw new Error(); }
}
}
static boolean doThrow = false;
static void $noinline$staticMethod() {
- if (doThrow) {
// Try defeating inlining.
- throw new Error();
- }
+ if (doThrow) { throw new Error(); }
}
static {
static void constClassAndInvokeStatic(Iterable<?> it) {
$opt$inline$ignoreClass(ClassWithClinit7.class);
- ClassWithClinit7.someStaticMethod(it);
+ ClassWithClinit7.$noinline$someStaticMethod(it);
}
static void $opt$inline$ignoreClass(Class<?> c) {
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(""); }
}
}
static void sgetAndInvokeStatic(Iterable<?> it) {
$opt$inline$ignoreInt(ClassWithClinit8.value);
- ClassWithClinit8.someStaticMethod(it);
+ ClassWithClinit8.$noinline$someStaticMethod(it);
}
static void $opt$inline$ignoreInt(int i) {
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(""); }
}
}
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 {
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(""); }
}
}
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(""); }
}
}
}
}
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(""); }
+ }
}
}
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(""); }
}
}
}
}
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(""); }
}
}
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(""); }
}
}
--- /dev/null
+Checker test that we inline interface calls and if we can't inline
+them, we can turn them into a virtual invoke.
--- /dev/null
+/*
+ * 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();
+}