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() {}
+}