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