Fix invokeinterface sharpened with kRuntimeCall.

Bug: 174260111
Bug: 173677667

Test: 728-imt-conflict-zygote
Test: atest com.android.bootimageprofile.BootImageProfileTest#testSystemServerProfile
Test: adb install com.google.android.art.apex
Change-Id: Ie600a0c8c8eb38d9084b796bac9184c06ea0a2f4
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index f90c092..efa65fc 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4436,11 +4436,15 @@
   if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRecursive) {
     Location interface_method = locations->InAt(invoke->GetNumberOfArguments() - 1);
     if (interface_method.IsStackSlot()) {
-      __ Ldr(ip1, StackOperandFrom(receiver));
+      __ Ldr(ip1, StackOperandFrom(interface_method));
     } else {
       __ Mov(ip1, XRegisterFrom(interface_method));
     }
-  } else {
+  // If the load kind is through a runtime call, we will pass the method we
+  // fetch the IMT, which will either be a no-op if we don't hit the conflict
+  // stub, or will make us always go through the trampoline when there is a
+  // conflict.
+  } else if (invoke->GetHiddenArgumentLoadKind() != MethodLoadKind::kRuntimeCall) {
     codegen_->LoadMethod(
         invoke->GetHiddenArgumentLoadKind(), Location::RegisterLocation(ip1.GetCode()), invoke);
   }
@@ -4451,6 +4455,11 @@
       invoke->GetImtIndex(), kArm64PointerSize));
   // temp = temp->GetImtEntryAt(method_offset);
   __ Ldr(temp, MemOperand(temp, method_offset));
+  if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRuntimeCall) {
+    // We pass the method from the IMT in case of a conflict. This will ensure
+    // we go into the runtime to resolve the actual method.
+    __ Mov(ip1, temp);
+  }
   // lr = temp->GetEntryPoint();
   __ Ldr(lr, MemOperand(temp, entry_point.Int32Value()));
 
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index fcc4e06..cdd5d22 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -3540,6 +3540,11 @@
       } else {
         __ Mov(RegisterFrom(hidden_reg), RegisterFrom(current_method));
       }
+    } else if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRuntimeCall) {
+      // We pass the method from the IMT in case of a conflict. This will ensure
+      // we go into the runtime to resolve the actual method.
+      CHECK_NE(temp.GetCode(), lr.GetCode());
+      __ Mov(RegisterFrom(hidden_reg), temp);
     } else {
       codegen_->LoadMethod(invoke->GetHiddenArgumentLoadKind(), hidden_reg, invoke);
     }
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 7075d38..f6c0270 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -2621,7 +2621,7 @@
   DCHECK_EQ(XMM7, hidden_reg);
   if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRecursive) {
     __ movd(hidden_reg, locations->InAt(invoke->GetNumberOfArguments() - 1).AsRegister<Register>());
-  } else {
+  } else if (invoke->GetHiddenArgumentLoadKind() != MethodLoadKind::kRuntimeCall) {
     codegen_->LoadMethod(invoke->GetHiddenArgumentLoadKind(), locations->GetTemp(0), invoke);
     __ movd(hidden_reg, temp);
   }
@@ -2653,6 +2653,11 @@
   uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
       invoke->GetImtIndex(), kX86PointerSize));
   __ movl(temp, Address(temp, method_offset));
+  if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRuntimeCall) {
+    // We pass the method from the IMT in case of a conflict. This will ensure
+    // we go into the runtime to resolve the actual method.
+    __ movd(hidden_reg, temp);
+  }
   // call temp->GetEntryPoint();
   __ call(Address(temp,
                   ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86PointerSize).Int32Value()));
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 1fc4c18..d79c2e4 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2808,11 +2808,12 @@
 
   codegen_->MaybeGenerateInlineCacheCheck(invoke, temp);
 
-  if (invoke->GetHiddenArgumentLoadKind() != MethodLoadKind::kRecursive) {
+  if (invoke->GetHiddenArgumentLoadKind() != MethodLoadKind::kRecursive &&
+      invoke->GetHiddenArgumentLoadKind() != MethodLoadKind::kRuntimeCall) {
     Location hidden_reg = locations->GetTemp(1);
     // Set the hidden argument. This is safe to do this here, as RAX
     // won't be modified thereafter, before the `call` instruction.
-    // We also di it after MaybeGenerateInlineCache that may use RAX.
+    // We also do it after MaybeGenerateInlineCache that may use RAX.
     DCHECK_EQ(RAX, hidden_reg.AsRegister<Register>());
     codegen_->LoadMethod(invoke->GetHiddenArgumentLoadKind(), hidden_reg, invoke);
   }
@@ -2825,6 +2826,12 @@
       invoke->GetImtIndex(), kX86_64PointerSize));
   // temp = temp->GetImtEntryAt(method_offset);
   __ movq(temp, Address(temp, method_offset));
+  if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRuntimeCall) {
+    // We pass the method from the IMT in case of a conflict. This will ensure
+    // we go into the runtime to resolve the actual method.
+    Location hidden_reg = locations->GetTemp(1);
+    __ movq(hidden_reg.AsRegister<CpuRegister>(), temp);
+  }
   // call temp->GetEntryPoint();
   __ call(Address(
       temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86_64PointerSize).SizeValue()));
diff --git a/test/728-imt-conflict-zygote/expected-stderr.txt b/test/728-imt-conflict-zygote/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/728-imt-conflict-zygote/expected-stderr.txt
diff --git a/test/728-imt-conflict-zygote/expected-stdout.txt b/test/728-imt-conflict-zygote/expected-stdout.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/728-imt-conflict-zygote/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/728-imt-conflict-zygote/info.txt b/test/728-imt-conflict-zygote/info.txt
new file mode 100644
index 0000000..85a67f3
--- /dev/null
+++ b/test/728-imt-conflict-zygote/info.txt
@@ -0,0 +1,2 @@
+Regression test for the compiler in the case we need to do a runtime call to
+lookup an ArtMethod for an invokeinterface.
diff --git a/test/728-imt-conflict-zygote/run b/test/728-imt-conflict-zygote/run
new file mode 100644
index 0000000..8fdff6d
--- /dev/null
+++ b/test/728-imt-conflict-zygote/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2020 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.
+
+./default-run "$@" --zygote
diff --git a/test/728-imt-conflict-zygote/src/Main.java b/test/728-imt-conflict-zygote/src/Main.java
new file mode 100644
index 0000000..5a88679
--- /dev/null
+++ b/test/728-imt-conflict-zygote/src/Main.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+import java.lang.reflect.Method;
+
+// An interface with enough methods to trigger a conflict.
+interface Itf {
+  public void method0a();
+  public void method0b();
+  public void method0c();
+  public void method0d();
+  public void method0e();
+  public void method0f();
+  public void method0g();
+  public void method0h();
+  public void method0i();
+  public void method0j();
+  public void method0k();
+  public void method0l();
+  public void method0m();
+  public void method0n();
+  public void method0o();
+  public void method0p();
+  public void method0q();
+  public void method0r();
+  public void method0s();
+  public void method0t();
+  public void method0u();
+  public void method0v();
+  public void method0w();
+  public void method0x();
+  public void method0y();
+  public void method0z();
+  public void method1a();
+  public void method1b();
+  public void method1c();
+  public void method1d();
+  public void method1e();
+  public void method1f();
+  public void method1g();
+  public void method1h();
+  public void method1i();
+  public void method1j();
+  public void method1k();
+  public void method1l();
+  public void method1m();
+  public void method1n();
+  public void method1o();
+  public void method1p();
+  public void method1q();
+  public void method1r();
+  public void method1s();
+  public void method1t();
+  public void method1u();
+  public void method1v();
+  public void method1w();
+  public void method1x();
+  public void method1y();
+  public void method1z();
+  public void method2a();
+  public void method2b();
+  public void method2c();
+  public void method2d();
+  public void method2e();
+  public void method2f();
+  public void method2g();
+  public void method2h();
+  public void method2i();
+  public void method2j();
+  public void method2k();
+  public void method2l();
+  public void method2m();
+  public void method2n();
+  public void method2o();
+  public void method2p();
+  public void method2q();
+  public void method2r();
+  public void method2s();
+  public void method2t();
+  public void method2u();
+  public void method2v();
+  public void method2w();
+  public void method2x();
+  public void method2y();
+  public void method2z();
+}
+
+public class Main implements Itf {
+  public static Itf main;
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+    ensureJitCompiled(Main.class, "$noinline$callInterfaceMethods");
+    $noinline$callInterfaceMethods(new Main());
+  }
+
+  public static native void ensureJitCompiled(Class<?> cls, String name);
+
+  public static void $noinline$callInterfaceMethods(Itf itf) {
+    itf.method0a();
+    itf.method0b();
+    itf.method0c();
+    itf.method0d();
+    itf.method0e();
+    itf.method0f();
+    itf.method0g();
+    itf.method0h();
+    itf.method0i();
+    itf.method0j();
+    itf.method0k();
+    itf.method0l();
+    itf.method0m();
+    itf.method0n();
+    itf.method0o();
+    itf.method0p();
+    itf.method0q();
+    itf.method0r();
+    itf.method0s();
+    itf.method0t();
+    itf.method0u();
+    itf.method0v();
+    itf.method0w();
+    itf.method0x();
+    itf.method0y();
+    itf.method0z();
+
+    itf.method1a();
+    itf.method1b();
+    itf.method1c();
+    itf.method1d();
+    itf.method1e();
+    itf.method1f();
+    itf.method1g();
+    itf.method1h();
+    itf.method1i();
+    itf.method1j();
+    itf.method1k();
+    itf.method1l();
+    itf.method1m();
+    itf.method1n();
+    itf.method1o();
+    itf.method1p();
+    itf.method1q();
+    itf.method1r();
+    itf.method1s();
+    itf.method1t();
+    itf.method1u();
+    itf.method1v();
+    itf.method1w();
+    itf.method1x();
+    itf.method1y();
+    itf.method1z();
+  }
+
+  public void method0a() {}
+  public void method0b() {}
+  public void method0c() {}
+  public void method0d() {}
+  public void method0e() {}
+  public void method0f() {}
+  public void method0g() {}
+  public void method0h() {}
+  public void method0i() {}
+  public void method0j() {}
+  public void method0k() {}
+  public void method0l() {}
+  public void method0m() {}
+  public void method0n() {}
+  public void method0o() {}
+  public void method0p() {}
+  public void method0q() {}
+  public void method0r() {}
+  public void method0s() {}
+  public void method0t() {}
+  public void method0u() {}
+  public void method0v() {}
+  public void method0w() {}
+  public void method0x() {}
+  public void method0y() {}
+  public void method0z() {}
+  public void method1a() {}
+  public void method1b() {}
+  public void method1c() {}
+  public void method1d() {}
+  public void method1e() {}
+  public void method1f() {}
+  public void method1g() {}
+  public void method1h() {}
+  public void method1i() {}
+  public void method1j() {}
+  public void method1k() {}
+  public void method1l() {}
+  public void method1m() {}
+  public void method1n() {}
+  public void method1o() {}
+  public void method1p() {}
+  public void method1q() {}
+  public void method1r() {}
+  public void method1s() {}
+  public void method1t() {}
+  public void method1u() {}
+  public void method1v() {}
+  public void method1w() {}
+  public void method1x() {}
+  public void method1y() {}
+  public void method1z() {}
+  public void method2a() {}
+  public void method2b() {}
+  public void method2c() {}
+  public void method2d() {}
+  public void method2e() {}
+  public void method2f() {}
+  public void method2g() {}
+  public void method2h() {}
+  public void method2i() {}
+  public void method2j() {}
+  public void method2k() {}
+  public void method2l() {}
+  public void method2m() {}
+  public void method2n() {}
+  public void method2o() {}
+  public void method2p() {}
+  public void method2q() {}
+  public void method2r() {}
+  public void method2s() {}
+  public void method2t() {}
+  public void method2u() {}
+  public void method2v() {}
+  public void method2w() {}
+  public void method2x() {}
+  public void method2y() {}
+  public void method2z() {}
+}