Reland "Devirtualize to HInvokeStaticOrDirect."
This reverts commit 39d4df62d4e2606073d05cc363370db825ad7b9f.
Reason for revert: fix JIT-zygote issue.
Test: JIT zygote boots.
Change-Id: I895ad8e59e472fb662ca9bc5394c2fd9c6babc74
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 7848ec2..d869991 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -530,9 +530,6 @@
"name": "art-run-test-449-checker-bce-rem[com.google.android.art.apex]"
},
{
- "name": "art-run-test-450-checker-types[com.google.android.art.apex]"
- },
- {
"name": "art-run-test-451-regression-add-float[com.google.android.art.apex]"
},
{
@@ -1387,9 +1384,6 @@
"name": "art-run-test-449-checker-bce-rem"
},
{
- "name": "art-run-test-450-checker-types"
- },
- {
"name": "art-run-test-451-regression-add-float"
},
{
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 611c691..9e1965d 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1240,32 +1240,22 @@
bool HInliner::TryDevirtualize(HInvoke* invoke_instruction,
ArtMethod* method,
HInvoke** replacement) {
- DCHECK(!method->IsProxyMethod());
DCHECK(invoke_instruction != *replacement);
- if (!invoke_instruction->IsInvokeInterface()) {
- // TODO: Consider sharpening an invoke virtual once it is not dependent on the
- // compiler driver.
+ if (!invoke_instruction->IsInvokeInterface() && !invoke_instruction->IsInvokeVirtual()) {
return false;
}
- // Devirtualization by exact type uses a method in the vtable, so we should
- // not see a default non-copied method.
- DCHECK(!method->IsDefault() || method->IsCopied());
- // 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.
- if (kIsDebugBuild && method->IsDefaultConflicting()) {
- ReferenceTypeInfo receiver_type = invoke_instruction->InputAt(0)->GetReferenceTypeInfo();
- // Devirtualization by exact type uses a method in the vtable,
- // so it's OK to change this invoke into a HInvokeVirtual.
- ObjPtr<mirror::Class> receiver_class = receiver_type.GetTypeHandle().Get();
- CHECK(!receiver_class->IsInterface());
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- CHECK(method == receiver_class->GetVTableEntry(method->GetMethodIndex(), pointer_size));
+ // Don't bother trying to call directly a default conflict method. It
+ // doesn't have a proper MethodReference, but also `GetCanonicalMethod`
+ // will return an actual default implementation.
+ if (method->IsDefaultConflicting()) {
+ return false;
}
-
+ DCHECK(!method->IsProxyMethod());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ PointerSize pointer_size = cl->GetImagePointerSize();
+ // The sharpening logic assumes the caller isn't passing a copied method.
+ method = method->GetCanonicalMethod(pointer_size);
uint32_t dex_method_index = FindMethodIndexIn(
method,
*invoke_instruction->GetMethodReference().dex_file,
@@ -1273,19 +1263,40 @@
if (dex_method_index == dex::kDexNoIndex) {
return false;
}
- HInvokeVirtual* new_invoke = new (graph_->GetAllocator()) HInvokeVirtual(
+ HInvokeStaticOrDirect::DispatchInfo dispatch_info =
+ HSharpening::SharpenLoadMethod(method,
+ /* has_method_id= */ true,
+ /* for_interface_call= */ false,
+ codegen_);
+ DCHECK_NE(dispatch_info.code_ptr_location, CodePtrLocation::kCallCriticalNative);
+ if (dispatch_info.method_load_kind == MethodLoadKind::kRuntimeCall) {
+ // If sharpening returns that we need to load the method at runtime, keep
+ // the virtual/interface call which will be faster.
+ // Also, the entrypoints for runtime calls do not handle devirtualized
+ // calls.
+ return false;
+ }
+
+ HInvokeStaticOrDirect* new_invoke = new (graph_->GetAllocator()) HInvokeStaticOrDirect(
graph_->GetAllocator(),
invoke_instruction->GetNumberOfArguments(),
invoke_instruction->GetType(),
invoke_instruction->GetDexPc(),
MethodReference(invoke_instruction->GetMethodReference().dex_file, dex_method_index),
method,
+ dispatch_info,
+ kDirect,
MethodReference(method->GetDexFile(), method->GetDexMethodIndex()),
- method->GetMethodIndex());
+ HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
HInputsRef inputs = invoke_instruction->GetInputs();
+ DCHECK_EQ(inputs.size(), invoke_instruction->GetNumberOfArguments());
for (size_t index = 0; index != inputs.size(); ++index) {
new_invoke->SetArgumentAt(index, inputs[index]);
}
+ if (HInvokeStaticOrDirect::NeedsCurrentMethodInput(dispatch_info)) {
+ new_invoke->SetRawInputAt(new_invoke->GetCurrentMethodIndexUnchecked(),
+ graph_->GetCurrentMethod());
+ }
invoke_instruction->GetBlock()->InsertInstructionBefore(new_invoke, invoke_instruction);
new_invoke->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
if (invoke_instruction->GetType() == DataType::Type::kReference) {
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index ef0c474..1c0127a 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -302,6 +302,10 @@
oat_file->GetBssMethods().data() + oat_file->GetBssMethods().size());
std::atomic<ArtMethod*>* atomic_entry =
reinterpret_cast<std::atomic<ArtMethod*>*>(method_entry);
+ if (kIsDebugBuild) {
+ ArtMethod* existing = atomic_entry->load(std::memory_order_acquire);
+ CHECK(existing->IsRuntimeMethod() || existing == callee);
+ }
static_assert(sizeof(*method_entry) == sizeof(*atomic_entry), "Size check.");
atomic_entry->store(callee, std::memory_order_release);
}
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index bc5a10d..eaa9f4e 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1357,7 +1357,7 @@
// odd situation where the ArtMethod being executed is unrelated to the
// receiver of the method.
called = called->GetCanonicalMethod();
- if (invoke_type == kSuper) {
+ if (invoke_type == kSuper || invoke_type == kInterface || invoke_type == kVirtual) {
if (called->GetDexFile() == called_method.dex_file) {
called_method.index = called->GetDexMethodIndex();
} else {
diff --git a/test/450-checker-types/Android.bp b/test/450-checker-types/Android.bp
index bf45226..1173b5a 100644
--- a/test/450-checker-types/Android.bp
+++ b/test/450-checker-types/Android.bp
@@ -15,7 +15,7 @@
java_test {
name: "art-run-test-450-checker-types",
defaults: ["art-run-test-defaults"],
- test_config_template: ":art-run-test-target-template",
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
srcs: ["src/**/*.java"],
data: [
":art-run-test-450-checker-types-expected-stdout",
diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java
index 9e3f951..084a713 100644
--- a/test/450-checker-types/src/Main.java
+++ b/test/450-checker-types/src/Main.java
@@ -686,7 +686,7 @@
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<Phi:l\d+>> Phi [<<Arg>>,<<Null>>] klass:SubclassA
/// CHECK-DAG: <<NCPhi:l\d+>> NullCheck [<<Phi>>]
- /// CHECK-DAG: InvokeVirtual [<<NCPhi>>] method_name:java.lang.Object.hashCode
+ /// CHECK-DAG: InvokeStaticOrDirect [<<NCPhi>>] method_name:java.lang.Object.hashCode
public void testThisArgumentMoreSpecific(boolean cond) {
// Inlining method from Super will build it with `this` typed as Super.
diff --git a/test/569-checker-pattern-replacement/src/Main.java b/test/569-checker-pattern-replacement/src/Main.java
index bcf8cdd..e2658a4 100644
--- a/test/569-checker-pattern-replacement/src/Main.java
+++ b/test/569-checker-pattern-replacement/src/Main.java
@@ -209,10 +209,11 @@
/// CHECK: {{d\d+}} InvokeVirtual
/// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
- /// CHECK: {{d\d+}} InvokeVirtual
+ /// CHECK: {{d\d+}} InvokeStaticOrDirect
/// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
/// CHECK-NOT: InstanceFieldGet
+ /// CHECK-NOT: InvokeVirtual
public static double getDoubleFromParam(Second s) {
return s.getInstanceDoubleFieldFromParam(s);
@@ -222,11 +223,12 @@
/// CHECK: {{i\d+}} InvokeVirtual
/// CHECK-START: int Main.getStaticInt(Second) inliner (after)
- /// CHECK: {{i\d+}} InvokeVirtual
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
/// CHECK-START: int Main.getStaticInt(Second) inliner (after)
/// CHECK-NOT: InstanceFieldGet
/// CHECK-NOT: StaticFieldGet
+ /// CHECK-NOT: InvokeVirtual
public static int getStaticInt(Second s) {
return s.getStaticIntField();
@@ -287,10 +289,11 @@
/// CHECK: InvokeVirtual
/// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
- /// CHECK: InvokeVirtual
+ /// CHECK: InvokeStaticOrDirect
/// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
/// CHECK-NOT: InstanceFieldSet
+ /// CHECK-NOT: InvokeVirtual
public static long setLongThroughParam(Second s, long value) {
s.setInstanceLongFieldThroughParam(s, value);
@@ -301,11 +304,12 @@
/// CHECK: InvokeVirtual
/// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
- /// CHECK: InvokeVirtual
+ /// CHECK: InvokeStaticOrDirect
/// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: StaticFieldSet
+ /// CHECK-NOT: InvokeVirtual
public static float setStaticFloat(Second s, float value) {
s.setStaticFloatField(value);
diff --git a/test/609-checker-inline-interface/src/Main.java b/test/609-checker-inline-interface/src/Main.java
index 249b778..07709ea 100644
--- a/test/609-checker-inline-interface/src/Main.java
+++ b/test/609-checker-inline-interface/src/Main.java
@@ -32,7 +32,7 @@
// Expected
}
try {
- testInterfaceToVirtualCall();
+ testInterfaceToDirectCall();
} catch (Error e) {
// Expected.
}
@@ -53,19 +53,19 @@
methodWithInvokeInterface(itf);
}
- /// CHECK-START: void Main.testInterfaceToVirtualCall() inliner (before)
+ /// CHECK-START: void Main.testInterfaceToDirectCall() inliner (before)
/// CHECK: InvokeStaticOrDirect method_name:Main.methodWithInvokeInterface
- /// CHECK-START: void Main.testInterfaceToVirtualCall() inliner (before)
+ /// CHECK-START: void Main.testInterfaceToDirectCall() inliner (before)
/// CHECK-NOT: InvokeInterface
- /// CHECK-START: void Main.testInterfaceToVirtualCall() inliner (after)
- /// CHECK: InvokeVirtual method_name:Main.doCall
+ /// CHECK-START: void Main.testInterfaceToDirectCall() inliner (after)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.doCall
- /// CHECK-START: void Main.testInterfaceToVirtualCall() inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: void Main.testInterfaceToDirectCall() inliner (after)
+ /// CHECK-NOT: InvokeVirtual
/// CHECK-NOT: InvokeInterface
- public static void testInterfaceToVirtualCall() {
+ public static void testInterfaceToDirectCall() {
methodWithInvokeInterface(m);
}
diff --git a/test/673-checker-throw-vmethod/src/Main.java b/test/673-checker-throw-vmethod/src/Main.java
index f62cfc8..3979300 100644
--- a/test/673-checker-throw-vmethod/src/Main.java
+++ b/test/673-checker-throw-vmethod/src/Main.java
@@ -81,7 +81,7 @@
/// CHECK: If [<<Tst>>]
/// CHECK: end_block
/// CHECK: begin_block
- /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] method_name:Main.doThrow
+ /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:Main.doThrow
/// CHECK: end_block
//
/// CHECK-START: void Main.doit2(int[]) code_sinking (after)
@@ -91,7 +91,7 @@
/// CHECK: end_block
/// CHECK: begin_block
/// CHECK: <<Str:l\d+>> LoadString
- /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] method_name:Main.doThrow
+ /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:Main.doThrow
/// CHECK: end_block
public void doit2(int[] a) {
// Being in the boot image means we know the load string cannot throw. Create one that is
@@ -142,7 +142,7 @@
/// CHECK: If [<<Tst>>]
/// CHECK: end_block
/// CHECK: begin_block
- /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] method_name:Main.doThrow
+ /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:Main.doThrow
/// CHECK: end_block
//
/// CHECK-START: void Main.doit4(int[]) code_sinking (after)
@@ -152,7 +152,7 @@
/// CHECK: end_block
/// CHECK: begin_block
/// CHECK: <<Str:l\d+>> LoadString
- /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] method_name:Main.doThrow
+ /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:Main.doThrow
/// CHECK: end_block
public void doit4(int[] a) {
// Being in the boot image means we know the load string cannot throw. Create one that is
diff --git a/test/730-checker-inlining-super/expected-stderr.txt b/test/730-checker-inlining-super/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/730-checker-inlining-super/expected-stderr.txt
diff --git a/test/730-checker-inlining-super/expected-stdout.txt b/test/730-checker-inlining-super/expected-stdout.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/730-checker-inlining-super/expected-stdout.txt
diff --git a/test/730-checker-inlining-super/info.txt b/test/730-checker-inlining-super/info.txt
new file mode 100644
index 0000000..d34596a
--- /dev/null
+++ b/test/730-checker-inlining-super/info.txt
@@ -0,0 +1,2 @@
+Test that an invoke-virtual devirtualized by the compiler uses the right bss
+slot at runtime.
diff --git a/test/730-checker-inlining-super/src/Main.java b/test/730-checker-inlining-super/src/Main.java
new file mode 100644
index 0000000..5a1b574
--- /dev/null
+++ b/test/730-checker-inlining-super/src/Main.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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 SuperClass {
+ public int doInvoke() {
+ synchronized (this) {
+ return 42;
+ }
+ }
+}
+
+public class Main extends SuperClass {
+
+ public static void main(String[] args) {
+ Main m = new Main();
+ int value = doInvokeTypedSuperClass(m);
+ if (value != 43) {
+ throw new Error("Expected 43, got " + value);
+ }
+ }
+
+ public static int doInvokeTypedSuperClass(SuperClass sc) {
+ return sc.doInvoke();
+ }
+
+ public int doInvoke() {
+ synchronized (this) {
+ return super.doInvoke() + 1;
+ }
+ }
+}
diff --git a/test/823-cha-inlining/src/Main.java b/test/823-cha-inlining/src/Main.java
index e25047d..c683e6b 100644
--- a/test/823-cha-inlining/src/Main.java
+++ b/test/823-cha-inlining/src/Main.java
@@ -92,7 +92,7 @@
$noinline$doCallDefaultConflictItf3();
throw new Error("Expected IncompatibleClassChangeError");
} catch (Exception e) {
- throw new Error("Unexpected exception");
+ throw new Error("Unexpected exception " + e);
} catch (IncompatibleClassChangeError e) {
// Expected.
}
diff --git a/test/utils/regen-test-files b/test/utils/regen-test-files
index cd9de74..1d81e31 100755
--- a/test/utils/regen-test-files
+++ b/test/utils/regen-test-files
@@ -114,6 +114,7 @@
"177-visibly-initialized-deadlock",
"178-app-image-native-method",
"179-nonvirtual-jni",
+ "450-checker-types",
"1900-track-alloc",
"1901-get-bytecodes",
"1902-suspend",