Prevent entering IMT conflict trampoline with j.l.Object methods.

This ensures that only interface methods enter the trampoline.

Test: 725-imt-conflict-object
Change-Id: Id730d921f213ee0f6d927dea5df69d57be431df0
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 4ae94b7..1e7b48e 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1002,14 +1002,27 @@
                                              resolved_method->GetMethodIndex());
   } else {
     DCHECK_EQ(invoke_type, kInterface);
-    ScopedObjectAccess soa(Thread::Current());  // Needed for the IMT index.
-    invoke = new (allocator_) HInvokeInterface(allocator_,
+    ScopedObjectAccess soa(Thread::Current());  // Needed for the IMT index and class check below.
+    if (resolved_method->GetDeclaringClass()->IsObjectClass()) {
+      // If the resolved method is from j.l.Object, emit a virtual call instead.
+      // The IMT conflict stub only handles interface methods.
+      invoke = new (allocator_) HInvokeVirtual(allocator_,
                                                number_of_arguments,
                                                return_type,
                                                dex_pc,
                                                method_idx,
                                                resolved_method,
-                                               ImTable::GetImtIndex(resolved_method));
+                                               resolved_method->GetMethodIndex());
+    } else {
+      DCHECK(resolved_method->GetDeclaringClass()->IsInterface());
+      invoke = new (allocator_) HInvokeInterface(allocator_,
+                                                 number_of_arguments,
+                                                 return_type,
+                                                 dex_pc,
+                                                 method_idx,
+                                                 resolved_method,
+                                                 ImTable::GetImtIndex(resolved_method));
+    }
   }
   return HandleInvoke(invoke, operands, shorty, /* is_unresolved= */ false, clinit_check);
 }
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 95c9ffc..5356637 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2453,6 +2453,11 @@
     }
   }
 
+  // The compiler and interpreter make sure the conflict trampoline is never
+  // called on a method that resolves to j.l.Object.
+  CHECK(!interface_method->GetDeclaringClass()->IsObjectClass());
+  CHECK(interface_method->GetDeclaringClass()->IsInterface());
+
   DCHECK(!interface_method->IsRuntimeMethod());
   // Look whether we have a match in the ImtConflictTable.
   uint32_t imt_index = interface_method->GetImtIndex();
diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc
index c14a5ff..36cd675 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -247,8 +247,16 @@
   }
 
   if (invoke_type == kInterface) {
-    UpdateCache(self, dex_pc_ptr, resolved_method->GetImtIndex());
-    return resolved_method->GetImtIndex();
+    if (resolved_method->GetDeclaringClass()->IsObjectClass()) {
+      // Don't update the cache and return a value with high bit set to notify the
+      // interpreter it should do a vtable call instead.
+      DCHECK_LT(resolved_method->GetMethodIndex(), 0x10000);
+      return resolved_method->GetMethodIndex() | (1 << 31);
+    } else {
+      DCHECK(resolved_method->GetDeclaringClass()->IsInterface());
+      UpdateCache(self, dex_pc_ptr, resolved_method->GetImtIndex());
+      return resolved_method->GetImtIndex();
+    }
   } else if (resolved_method->GetDeclaringClass()->IsStringClass()
              && !resolved_method->IsStatic()
              && resolved_method->IsConstructor()) {
diff --git a/runtime/interpreter/mterp/x86_64ng/invoke.S b/runtime/interpreter/mterp/x86_64ng/invoke.S
index 64d0623..dba1caa 100644
--- a/runtime/interpreter/mterp/x86_64ng/invoke.S
+++ b/runtime/interpreter/mterp/x86_64ng/invoke.S
@@ -87,7 +87,15 @@
    movq 0(%rsp), %rsi
    movq rPC, %rdx
    call nterp_get_method
-   jmp 1b
+   testl %eax, %eax
+   jns 1b
+   // For j.l.Object interface calls, the high bit is set. Also the method index is 16bits.
+   andl LITERAL(0xffff), %eax
+   .if $range
+   jmp NterpHandleInvokeInterfaceOnObjectMethodRange
+   .else
+   jmp NterpHandleInvokeInterfaceOnObjectMethod
+   .endif
 
 %def op_invoke_interface():
 %  invoke_interface(helper="NterpCommonInvokeInterface", range="0")
diff --git a/runtime/interpreter/mterp/x86_64ng/main.S b/runtime/interpreter/mterp/x86_64ng/main.S
index 11f89db..4eaf95e 100644
--- a/runtime/interpreter/mterp/x86_64ng/main.S
+++ b/runtime/interpreter/mterp/x86_64ng/main.S
@@ -1905,6 +1905,25 @@
     // Jump to the compiled code.
     jmp *%rbx
 
+NterpHandleInvokeInterfaceOnObjectMethodRange:
+   // First argument is the 'this' pointer.
+   movzwl 4(rPC), %r11d // arguments
+   movl (rFP, %r11, 4), %esi
+   // Note: if esi is null, this will be handled by our SIGSEGV handler.
+   movl MIRROR_OBJECT_CLASS_OFFSET(%esi), %edx
+   movq MIRROR_CLASS_VTABLE_OFFSET_64(%edx, %eax, 8), %rdi
+   jmp NterpCommonInvokeInstanceRange
+
+NterpHandleInvokeInterfaceOnObjectMethod:
+   // First argument is the 'this' pointer.
+   movzwl 4(rPC), %r11d // arguments
+   andq MACRO_LITERAL(0xf), %r11
+   movl (rFP, %r11, 4), %esi
+   // Note: if esi is null, this will be handled by our SIGSEGV handler.
+   movl MIRROR_OBJECT_CLASS_OFFSET(%esi), %edx
+   movq MIRROR_CLASS_VTABLE_OFFSET_64(%edx, %eax, 8), %rdi
+   jmp NterpCommonInvokeInstance
+
 // This is the logical end of ExecuteNterpImpl, where the frame info applies.
 // EndExecuteNterpImpl includes the methods below as we want the runtime to
 // see them as part of the Nterp PCs.
diff --git a/runtime/oat.h b/runtime/oat.h
index 6c739b2..206f8af 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
-  // Last oat version changed reason: pCompileOptimized entrypoint.
-  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '7', '8', '\0' } };
+  // Last oat version changed reason: invokeinterface on j.l.Object do a vtable call.
+  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '7', '9', '\0' } };
 
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/test/725-imt-conflict-object/expected.txt b/test/725-imt-conflict-object/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/725-imt-conflict-object/expected.txt
diff --git a/test/725-imt-conflict-object/info.txt b/test/725-imt-conflict-object/info.txt
new file mode 100644
index 0000000..db6345c
--- /dev/null
+++ b/test/725-imt-conflict-object/info.txt
@@ -0,0 +1,2 @@
+Test that invokeinterface through a j.l.Object method doesn't go
+through the IMT conflict trampoline.
diff --git a/test/725-imt-conflict-object/smali/TestCase.smali b/test/725-imt-conflict-object/smali/TestCase.smali
new file mode 100644
index 0000000..77665aa
--- /dev/null
+++ b/test/725-imt-conflict-object/smali/TestCase.smali
@@ -0,0 +1,25 @@
+# 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.
+
+.class public LTestCase;
+
+.super Ljava/lang/Object;
+
+.method public static test()Ljava/lang/String;
+   .registers 2
+   sget-object v0, LMain;->main:LItf;
+   invoke-interface {v0}, LItf;->toString()Ljava/lang/String;
+   move-result-object v0
+   return-object v0
+.end method
diff --git a/test/725-imt-conflict-object/src/Main.java b/test/725-imt-conflict-object/src/Main.java
new file mode 100644
index 0000000..58320b2
--- /dev/null
+++ b/test/725-imt-conflict-object/src/Main.java
@@ -0,0 +1,246 @@
+/*
+ * 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;
+
+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 void method3a();
+  public void method3b();
+  public void method3c();
+  public void method3d();
+  public void method3e();
+  public void method3f();
+  public void method3g();
+  public void method3h();
+  public void method3i();
+  public void method3j();
+  public void method3k();
+  public void method3l();
+  public void method3m();
+  public void method3n();
+  public void method3o();
+  public void method3p();
+  public void method3q();
+  public void method3r();
+  public void method3s();
+  public void method3t();
+  public void method3u();
+  public void method3v();
+  public void method3w();
+  public void method3x();
+  public void method3y();
+  public void method3z();
+}
+
+public class Main implements Itf {
+  public static Itf main;
+  public static void main(String[] args) throws Exception {
+    main = new Main();
+    Class<?> c = Class.forName("TestCase");
+    Method m = c.getMethod("test");
+    String result = (String)m.invoke(null);
+    if (!"MainInstance".equals(result)) {
+      throw new Error("Expected 'MainInstance', got '" + result + "'");
+    }
+  }
+
+  public String toString() {
+    return "MainInstance";
+  }
+
+  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 void method3a() {}
+  public void method3b() {}
+  public void method3c() {}
+  public void method3d() {}
+  public void method3e() {}
+  public void method3f() {}
+  public void method3g() {}
+  public void method3h() {}
+  public void method3i() {}
+  public void method3j() {}
+  public void method3k() {}
+  public void method3l() {}
+  public void method3m() {}
+  public void method3n() {}
+  public void method3o() {}
+  public void method3p() {}
+  public void method3q() {}
+  public void method3r() {}
+  public void method3s() {}
+  public void method3t() {}
+  public void method3u() {}
+  public void method3v() {}
+  public void method3w() {}
+  public void method3x() {}
+  public void method3y() {}
+  public void method3z() {}
+}