From 27ef25f084017421ca05508208f436b5fc11df73 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 24 Mar 2017 08:59:22 +0000 Subject: [PATCH] Revert "Revert^4 "CHA for interface method."" Still failing on the bots. This reverts commit fb185130f35bbfe2ba1875974ed39ef0b3c7a77f. Change-Id: I1629e237dd41ce4784ccadbfc346352dfcbdb137 --- compiler/optimizing/inliner.cc | 25 +-- runtime/art_method.h | 2 +- runtime/cha.cc | 114 ++----------- runtime/cha.h | 23 +-- test/616-cha-abstract/src/Main.java | 4 +- test/616-cha-interface-default/expected.txt | 1 - test/616-cha-interface-default/info.txt | 2 - test/616-cha-interface-default/multidex.jpp | 3 - test/616-cha-interface-default/run | 18 --- .../src-multidex/Base.java | 41 ----- test/616-cha-interface-default/src/Main.java | 176 --------------------- test/616-cha-interface/expected.txt | 1 - test/616-cha-interface/info.txt | 1 - test/616-cha-interface/run | 18 --- test/616-cha-interface/src/Main.java | 173 -------------------- test/616-cha-miranda/expected.txt | 1 - test/616-cha-miranda/info.txt | 1 - test/616-cha-miranda/run | 18 --- test/616-cha-miranda/src/Main.java | 163 ------------------- test/616-cha-proxy-method-inline/expected.txt | 1 - test/616-cha-proxy-method-inline/info.txt | 1 - test/616-cha-proxy-method-inline/multidex.jpp | 3 - test/616-cha-proxy-method-inline/run | 18 --- .../src-multidex/Foo.java | 19 --- test/616-cha-proxy-method-inline/src/Main.java | 70 -------- 25 files changed, 22 insertions(+), 875 deletions(-) delete mode 100644 test/616-cha-interface-default/expected.txt delete mode 100644 test/616-cha-interface-default/info.txt delete mode 100644 test/616-cha-interface-default/multidex.jpp delete mode 100644 test/616-cha-interface-default/run delete mode 100644 test/616-cha-interface-default/src-multidex/Base.java delete mode 100644 test/616-cha-interface-default/src/Main.java delete mode 100644 test/616-cha-interface/expected.txt delete mode 100644 test/616-cha-interface/info.txt delete mode 100644 test/616-cha-interface/run delete mode 100644 test/616-cha-interface/src/Main.java delete mode 100644 test/616-cha-miranda/expected.txt delete mode 100644 test/616-cha-miranda/info.txt delete mode 100644 test/616-cha-miranda/run delete mode 100644 test/616-cha-miranda/src/Main.java delete mode 100644 test/616-cha-proxy-method-inline/expected.txt delete mode 100644 test/616-cha-proxy-method-inline/info.txt delete mode 100644 test/616-cha-proxy-method-inline/multidex.jpp delete mode 100644 test/616-cha-proxy-method-inline/run delete mode 100644 test/616-cha-proxy-method-inline/src-multidex/Foo.java delete mode 100644 test/616-cha-proxy-method-inline/src/Main.java diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 9550a5333..62f5114e5 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -292,18 +292,7 @@ ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) { return nullptr; } PointerSize pointer_size = caller_compilation_unit_.GetClassLinker()->GetImagePointerSize(); - ArtMethod* single_impl = resolved_method->GetSingleImplementation(pointer_size); - if (single_impl == nullptr) { - return nullptr; - } - if (single_impl->IsProxyMethod()) { - // Proxy method is a generic invoker that's not worth - // devirtualizing/inlining. It also causes issues when the proxy - // method is in another dex file if we try to rewrite invoke-interface to - // invoke-virtual because a proxy method doesn't have a real dex file. - return nullptr; - } - return single_impl; + return resolved_method->GetSingleImplementation(pointer_size); } bool HInliner::TryInline(HInvoke* invoke_instruction) { @@ -1032,23 +1021,11 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) { if (invoke_instruction->IsInvokeInterface()) { - DCHECK(!method->IsProxyMethod()); // 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 (method->IsDefault() && !method->IsCopied()) { - // Changing to invoke-virtual cannot be done on an original default method - // since it's not in any vtable. Devirtualization by exact type/inline-cache - // always uses a method in the iftable which is never an original default - // method. - // On the other hand, inlining an original default method by CHA is fine. - DCHECK(cha_devirtualize); - return false; - } - const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); uint32_t dex_method_index = FindMethodIndexIn( method, caller_dex_file, invoke_instruction->GetDexMethodIndex()); diff --git a/runtime/art_method.h b/runtime/art_method.h index 8f09cc6d0..2248c3bd9 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -691,7 +691,7 @@ class ArtMethod FINAL { // Pointer to JNI function registered to this method, or a function to resolve the JNI function, // or the profiling data for non-native methods, or an ImtConflictTable, or the - // single-implementation of an abstract/interface method. + // single-implementation of an abstract method. void* data_; // Method dispatch from quick compiled code invokes this pointer which may cause bridging into diff --git a/runtime/cha.cc b/runtime/cha.cc index 7948c29e5..eaba01b2c 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -210,7 +210,7 @@ void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify } } -void ClassHierarchyAnalysis::CheckVirtualMethodSingleImplementationInfo( +void ClassHierarchyAnalysis::CheckSingleImplementationInfo( Handle klass, ArtMethod* virtual_method, ArtMethod* method_in_super, @@ -290,9 +290,8 @@ void ClassHierarchyAnalysis::CheckVirtualMethodSingleImplementationInfo( // A non-abstract method overrides an abstract method. if (method_in_super->GetSingleImplementation(pointer_size) == nullptr) { // Abstract method_in_super has no implementation yet. - // We need to grab cha_lock_ since there may be multiple class linking - // going on that can check/modify the single-implementation flag/method - // of method_in_super. + // We need to grab cha_lock_ for further checking/updating due to possible + // races. MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_); if (!method_in_super->HasSingleImplementation()) { return; @@ -363,55 +362,6 @@ void ClassHierarchyAnalysis::CheckVirtualMethodSingleImplementationInfo( } } -void ClassHierarchyAnalysis::CheckInterfaceMethodSingleImplementationInfo( - Handle klass, - ArtMethod* interface_method, - ArtMethod* implementation_method, - std::unordered_set& invalidated_single_impl_methods, - PointerSize pointer_size) { - DCHECK(klass->IsInstantiable()); - DCHECK(interface_method->IsAbstract() || interface_method->IsDefault()); - - if (!interface_method->HasSingleImplementation()) { - return; - } - - if (implementation_method->IsAbstract()) { - // An instantiable class doesn't supply an implementation for - // interface_method. Invoking the interface method on the class will throw - // AbstractMethodError. This is an uncommon case, so we simply treat - // interface_method as not having single-implementation. - invalidated_single_impl_methods.insert(interface_method); - return; - } - - // We need to grab cha_lock_ since there may be multiple class linking going - // on that can check/modify the single-implementation flag/method of - // interface_method. - MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_); - // Do this check again after we grab cha_lock_. - if (!interface_method->HasSingleImplementation()) { - return; - } - - ArtMethod* single_impl = interface_method->GetSingleImplementation(pointer_size); - if (single_impl == nullptr) { - // implementation_method becomes the first implementation for - // interface_method. - interface_method->SetSingleImplementation(implementation_method, pointer_size); - // Keep interface_method's single-implementation status. - return; - } - DCHECK(!single_impl->IsAbstract()); - if (single_impl->GetDeclaringClass() == implementation_method->GetDeclaringClass()) { - // Same implementation. Since implementation_method may be a copy of a default - // method, we need to check the declaring class for equality. - return; - } - // Another implementation for interface_method. - invalidated_single_impl_methods.insert(interface_method); -} - void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle klass, ArtMethod* method, PointerSize pointer_size) { @@ -432,7 +382,6 @@ void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle // Rare case, but we do accept it (such as 800-smali/smali/b_26143249.smali). // Do not attempt to devirtualize it. method->SetHasSingleImplementation(false); - DCHECK(method->GetSingleImplementation(pointer_size) == nullptr); } else { // Abstract method starts with single-implementation flag set and null // implementation method. @@ -447,15 +396,9 @@ void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle } void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle klass) { - PointerSize image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); if (klass->IsInterface()) { - for (ArtMethod& method : klass->GetDeclaredVirtualMethods(image_pointer_size)) { - DCHECK(method.IsAbstract() || method.IsDefault()); - InitSingleImplementationFlag(klass, &method, image_pointer_size); - } return; } - mirror::Class* super_class = klass->GetSuperClass(); if (super_class == nullptr) { return; @@ -465,6 +408,7 @@ void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle klass) { // is invalidated by linking `klass`. std::unordered_set invalidated_single_impl_methods; + PointerSize image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); // Do an entry-by-entry comparison of vtable contents with super's vtable. for (int32_t i = 0; i < super_class->GetVTableLength(); ++i) { ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size); @@ -474,59 +418,33 @@ void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle klass) { if (method->IsAbstract() && klass->IsInstantiable()) { // An instantiable class that inherits an abstract method is treated as // supplying an implementation that throws AbstractMethodError. - CheckVirtualMethodSingleImplementationInfo(klass, - method, - method_in_super, - invalidated_single_impl_methods, - image_pointer_size); + CheckSingleImplementationInfo(klass, + method, + method_in_super, + invalidated_single_impl_methods, + image_pointer_size); } continue; } InitSingleImplementationFlag(klass, method, image_pointer_size); - CheckVirtualMethodSingleImplementationInfo(klass, - method, - method_in_super, - invalidated_single_impl_methods, - image_pointer_size); + CheckSingleImplementationInfo(klass, + method, + method_in_super, + invalidated_single_impl_methods, + image_pointer_size); } + // For new virtual methods that don't override. for (int32_t i = super_class->GetVTableLength(); i < klass->GetVTableLength(); ++i) { ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size); InitSingleImplementationFlag(klass, method, image_pointer_size); } - if (klass->IsInstantiable()) { - auto* iftable = klass->GetIfTable(); - const size_t ifcount = klass->GetIfTableCount(); - for (size_t i = 0; i < ifcount; ++i) { - mirror::Class* interface = iftable->GetInterface(i); - for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) { - ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size); - mirror::PointerArray* method_array = iftable->GetMethodArray(i); - ArtMethod* implementation_method = - method_array->GetElementPtrSize(j, image_pointer_size); - DCHECK(implementation_method != nullptr) << klass->PrettyClass(); - CheckInterfaceMethodSingleImplementationInfo(klass, - interface_method, - implementation_method, - invalidated_single_impl_methods, - image_pointer_size); - } - } - } - - InvalidateSingleImplementationMethods(invalidated_single_impl_methods); -} - -void ClassHierarchyAnalysis::InvalidateSingleImplementationMethods( - std::unordered_set& invalidated_single_impl_methods) { + Runtime* const runtime = Runtime::Current(); if (!invalidated_single_impl_methods.empty()) { - Runtime* const runtime = Runtime::Current(); Thread *self = Thread::Current(); // Method headers for compiled code to be invalidated. std::unordered_set dependent_method_headers; - PointerSize image_pointer_size = - Runtime::Current()->GetClassLinker()->GetImagePointerSize(); { // We do this under cha_lock_. Committing code also grabs this lock to diff --git a/runtime/cha.h b/runtime/cha.h index 99c49d2bc..a56a752d8 100644 --- a/runtime/cha.h +++ b/runtime/cha.h @@ -117,13 +117,11 @@ class ClassHierarchyAnalysis { PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - // Check/update single-implementation info when one virtual method - // overrides another. // `virtual_method` in `klass` overrides `method_in_super`. - // This may invalidate some assumptions on single-implementation. + // This will invalidate some assumptions on single-implementation. // Append methods that should have their single-implementation flag invalidated // to `invalidated_single_impl_methods`. - void CheckVirtualMethodSingleImplementationInfo( + void CheckSingleImplementationInfo( Handle klass, ArtMethod* virtual_method, ArtMethod* method_in_super, @@ -131,23 +129,6 @@ class ClassHierarchyAnalysis { PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - // Check/update single-implementation info when one method - // implements an interface method. - // `implementation_method` in `klass` implements `interface_method`. - // Append `interface_method` to `invalidated_single_impl_methods` - // if `interface_method` gets a new implementation. - void CheckInterfaceMethodSingleImplementationInfo( - Handle klass, - ArtMethod* interface_method, - ArtMethod* implementation_method, - std::unordered_set& invalidated_single_impl_methods, - PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - - void InvalidateSingleImplementationMethods( - std::unordered_set& invalidated_single_impl_methods) - REQUIRES_SHARED(Locks::mutator_lock_); - // For all methods in vtable slot at `verify_index` of `verify_class` and its // superclasses, single-implementation status should be false, except if the // method is `excluded_method`. diff --git a/test/616-cha-abstract/src/Main.java b/test/616-cha-abstract/src/Main.java index b33f575de..e1d7db170 100644 --- a/test/616-cha-abstract/src/Main.java +++ b/test/616-cha-abstract/src/Main.java @@ -39,8 +39,8 @@ class Main2 extends Main1 { } public class Main { - static Base sMain1; - static Base sMain2; + static Main1 sMain1; + static Main1 sMain2; static boolean sIsOptimizing = true; static boolean sHasJIT = true; diff --git a/test/616-cha-interface-default/expected.txt b/test/616-cha-interface-default/expected.txt deleted file mode 100644 index 6a5618ebc..000000000 --- a/test/616-cha-interface-default/expected.txt +++ /dev/null @@ -1 +0,0 @@ -JNI_OnLoad called diff --git a/test/616-cha-interface-default/info.txt b/test/616-cha-interface-default/info.txt deleted file mode 100644 index 11baa1f0f..000000000 --- a/test/616-cha-interface-default/info.txt +++ /dev/null @@ -1,2 +0,0 @@ -Test for Class Hierarchy Analysis (CHA) on interface method. -Test it under multidex configuration to check cross-dex inlining. diff --git a/test/616-cha-interface-default/multidex.jpp b/test/616-cha-interface-default/multidex.jpp deleted file mode 100644 index b0d200ea3..000000000 --- a/test/616-cha-interface-default/multidex.jpp +++ /dev/null @@ -1,3 +0,0 @@ -Main: - @@com.android.jack.annotations.ForceInMainDex - class Main diff --git a/test/616-cha-interface-default/run b/test/616-cha-interface-default/run deleted file mode 100644 index d8b4f0d26..000000000 --- a/test/616-cha-interface-default/run +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2017 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. - -# Run without an app image to prevent the classes to be loaded at startup. -exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-interface-default/src-multidex/Base.java b/test/616-cha-interface-default/src-multidex/Base.java deleted file mode 100644 index 2cbcb500c..000000000 --- a/test/616-cha-interface-default/src-multidex/Base.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -interface Base { - default public int foo(int i) { - if (i != 1) { - return -2; - } - return i + 10; - } - - // Test default method that's not inlined. - default public int $noinline$bar() { - System.out.print(""); - System.out.print(""); - System.out.print(""); - System.out.print(""); - System.out.print(""); - System.out.print(""); - System.out.print(""); - System.out.print(""); - return -1; - } - - default void printError(String msg) { - System.out.println(msg); - } -} diff --git a/test/616-cha-interface-default/src/Main.java b/test/616-cha-interface-default/src/Main.java deleted file mode 100644 index 951607d2c..000000000 --- a/test/616-cha-interface-default/src/Main.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2017 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 Main1 implements Base { -} - -class Main2 extends Main1 { - public void foobar() {} -} - -class Main3 implements Base { - public int foo(int i) { - if (i != 3) { - printError("error3"); - } - return -(i + 10); - } -} - -public class Main { - static Base sMain1; - static Base sMain2; - static Base sMain3; - - static boolean sIsOptimizing = true; - static boolean sHasJIT = true; - static volatile boolean sOtherThreadStarted; - - private static void assertSingleImplementation(Class clazz, String method_name, boolean b) { - if (hasSingleImplementation(clazz, method_name) != b) { - System.out.println(clazz + "." + method_name + - " doesn't have single implementation value of " + b); - } - } - - static int getValue(Class cls) { - if (cls == Main1.class || cls == Main2.class) { - return 1; - } - return 3; - } - - // sMain1.foo()/sMain2.foo() will be always be Base.foo() before Main3 is loaded/linked. - // So sMain1.foo() can be devirtualized to Base.foo() and be inlined. - // After Dummy.createMain3() which links in Main3, live testImplement() on stack - // should be deoptimized. - static void testImplement(boolean createMain3, boolean wait, boolean setHasJIT) { - if (setHasJIT) { - if (isInterpreted()) { - sHasJIT = false; - } - return; - } - - if (createMain3 && (sIsOptimizing || sHasJIT)) { - assertIsManaged(); - } - - if (sMain1.foo(getValue(sMain1.getClass())) != 11) { - System.out.println("11 expected."); - } - if (sMain1.$noinline$bar() != -1) { - System.out.println("-1 expected."); - } - if (sMain2.foo(getValue(sMain2.getClass())) != 11) { - System.out.println("11 expected."); - } - - if (createMain3) { - // Wait for the other thread to start. - while (!sOtherThreadStarted); - // Create an Main2 instance and assign it to sMain2. - // sMain1 is kept the same. - sMain3 = Dummy.createMain3(); - // Wake up the other thread. - synchronized(Main.class) { - Main.class.notify(); - } - } else if (wait) { - // This is the other thread. - synchronized(Main.class) { - sOtherThreadStarted = true; - // Wait for Main2 to be linked and deoptimization is triggered. - try { - Main.class.wait(); - } catch (Exception e) { - } - } - } - - // There should be a deoptimization here right after Main3 is linked by - // calling Dummy.createMain3(), even though sMain1 didn't change. - // The behavior here would be different if inline-cache is used, which - // doesn't deoptimize since sMain1 still hits the type cache. - if (sMain1.foo(getValue(sMain1.getClass())) != 11) { - System.out.println("11 expected."); - } - if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) { - // This method should be deoptimized right after Main3 is created. - assertIsInterpreted(); - } - - if (sMain3 != null) { - if (sMain3.foo(getValue(sMain3.getClass())) != -13) { - System.out.println("-13 expected."); - } - } - } - - // Test scenarios under which CHA-based devirtualization happens, - // and class loading that implements a method can invalidate compiled code. - public static void main(String[] args) { - System.loadLibrary(args[0]); - - if (isInterpreted()) { - sIsOptimizing = false; - } - - // sMain1 is an instance of Main1. - // sMain2 is an instance of Main2. - // Neither Main1 nor Main2 override default method Base.foo(). - // Main3 hasn't bee loaded yet. - sMain1 = new Main1(); - sMain2 = new Main2(); - - ensureJitCompiled(Main.class, "testImplement"); - testImplement(false, false, true); - - if (sHasJIT && !sIsOptimizing) { - assertSingleImplementation(Base.class, "foo", true); - assertSingleImplementation(Main1.class, "foo", true); - } else { - // Main3 is verified ahead-of-time so it's linked in already. - } - - // Create another thread that also calls sMain1.foo(). - // Try to test suspend and deopt another thread. - new Thread() { - public void run() { - testImplement(false, true, false); - } - }.start(); - - // This will create Main3 instance in the middle of testImplement(). - testImplement(true, false, false); - assertSingleImplementation(Base.class, "foo", false); - assertSingleImplementation(Main1.class, "foo", true); - assertSingleImplementation(sMain3.getClass(), "foo", true); - } - - private static native void ensureJitCompiled(Class itf, String method_name); - private static native void assertIsInterpreted(); - private static native void assertIsManaged(); - private static native boolean isInterpreted(); - private static native boolean hasSingleImplementation(Class clazz, String method_name); -} - -// Put createMain3() in another class to avoid class loading due to verifier. -class Dummy { - static Base createMain3() { - return new Main3(); - } -} diff --git a/test/616-cha-interface/expected.txt b/test/616-cha-interface/expected.txt deleted file mode 100644 index 6a5618ebc..000000000 --- a/test/616-cha-interface/expected.txt +++ /dev/null @@ -1 +0,0 @@ -JNI_OnLoad called diff --git a/test/616-cha-interface/info.txt b/test/616-cha-interface/info.txt deleted file mode 100644 index 1fd330afd..000000000 --- a/test/616-cha-interface/info.txt +++ /dev/null @@ -1 +0,0 @@ -Test for Class Hierarchy Analysis (CHA) on interface method. diff --git a/test/616-cha-interface/run b/test/616-cha-interface/run deleted file mode 100644 index d8b4f0d26..000000000 --- a/test/616-cha-interface/run +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2017 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. - -# Run without an app image to prevent the classes to be loaded at startup. -exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-interface/src/Main.java b/test/616-cha-interface/src/Main.java deleted file mode 100644 index 3c9349663..000000000 --- a/test/616-cha-interface/src/Main.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -interface Base { - void foo(int i); - void $noinline$bar(); -} - -class Main1 implements Base { - public void foo(int i) { - if (i != 1) { - printError("error1"); - } - } - - // Test rewriting invoke-interface into invoke-virtual when inlining fails. - public void $noinline$bar() { - System.out.print(""); - System.out.print(""); - System.out.print(""); - System.out.print(""); - System.out.print(""); - System.out.print(""); - System.out.print(""); - System.out.print(""); - } - - void printError(String msg) { - System.out.println(msg); - } -} - -class Main2 extends Main1 { - public void foo(int i) { - if (i != 2) { - printError("error2"); - } - } -} - -public class Main { - static Base sMain1; - static Base sMain2; - - static boolean sIsOptimizing = true; - static boolean sHasJIT = true; - static volatile boolean sOtherThreadStarted; - - private static void assertSingleImplementation(Class clazz, String method_name, boolean b) { - if (hasSingleImplementation(clazz, method_name) != b) { - System.out.println(clazz + "." + method_name + - " doesn't have single implementation value of " + b); - } - } - - // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked. - // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined. - // After Dummy.createMain2() which links in Main2, live testImplement() on stack - // should be deoptimized. - static void testImplement(boolean createMain2, boolean wait, boolean setHasJIT) { - if (setHasJIT) { - if (isInterpreted()) { - sHasJIT = false; - } - return; - } - - if (createMain2 && (sIsOptimizing || sHasJIT)) { - assertIsManaged(); - } - - sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); - sMain1.$noinline$bar(); - - if (createMain2) { - // Wait for the other thread to start. - while (!sOtherThreadStarted); - // Create an Main2 instance and assign it to sMain2. - // sMain1 is kept the same. - sMain2 = Dummy.createMain2(); - // Wake up the other thread. - synchronized(Main.class) { - Main.class.notify(); - } - } else if (wait) { - // This is the other thread. - synchronized(Main.class) { - sOtherThreadStarted = true; - // Wait for Main2 to be linked and deoptimization is triggered. - try { - Main.class.wait(); - } catch (Exception e) { - } - } - } - - // There should be a deoptimization here right after Main2 is linked by - // calling Dummy.createMain2(), even though sMain1 didn't change. - // The behavior here would be different if inline-cache is used, which - // doesn't deoptimize since sMain1 still hits the type cache. - sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); - if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) { - // This method should be deoptimized right after Main2 is created. - assertIsInterpreted(); - } - - if (sMain2 != null) { - sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2); - } - } - - // Test scenarios under which CHA-based devirtualization happens, - // and class loading that overrides a method can invalidate compiled code. - public static void main(String[] args) { - System.loadLibrary(args[0]); - - if (isInterpreted()) { - sIsOptimizing = false; - } - - // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. - sMain1 = new Main1(); - - ensureJitCompiled(Main.class, "testImplement"); - testImplement(false, false, true); - - if (sHasJIT && !sIsOptimizing) { - assertSingleImplementation(Base.class, "foo", true); - assertSingleImplementation(Main1.class, "foo", true); - } else { - // Main2 is verified ahead-of-time so it's linked in already. - } - - // Create another thread that also calls sMain1.foo(). - // Try to test suspend and deopt another thread. - new Thread() { - public void run() { - testImplement(false, true, false); - } - }.start(); - - // This will create Main2 instance in the middle of testImplement(). - testImplement(true, false, false); - assertSingleImplementation(Base.class, "foo", false); - assertSingleImplementation(Main1.class, "foo", false); - } - - private static native void ensureJitCompiled(Class itf, String method_name); - private static native void assertIsInterpreted(); - private static native void assertIsManaged(); - private static native boolean isInterpreted(); - private static native boolean hasSingleImplementation(Class clazz, String method_name); -} - -// Put createMain2() in another class to avoid class loading due to verifier. -class Dummy { - static Main1 createMain2() { - return new Main2(); - } -} diff --git a/test/616-cha-miranda/expected.txt b/test/616-cha-miranda/expected.txt deleted file mode 100644 index 6a5618ebc..000000000 --- a/test/616-cha-miranda/expected.txt +++ /dev/null @@ -1 +0,0 @@ -JNI_OnLoad called diff --git a/test/616-cha-miranda/info.txt b/test/616-cha-miranda/info.txt deleted file mode 100644 index c46f33f61..000000000 --- a/test/616-cha-miranda/info.txt +++ /dev/null @@ -1 +0,0 @@ -Test for Class Hierarchy Analysis (CHA) on miranda method. diff --git a/test/616-cha-miranda/run b/test/616-cha-miranda/run deleted file mode 100644 index d8b4f0d26..000000000 --- a/test/616-cha-miranda/run +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2017 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. - -# Run without an app image to prevent the classes to be loaded at startup. -exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-miranda/src/Main.java b/test/616-cha-miranda/src/Main.java deleted file mode 100644 index e548482eb..000000000 --- a/test/616-cha-miranda/src/Main.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -interface Iface { - public void foo(int i); -} - -abstract class Base implements Iface { - // Iface.foo(int) will be added as a miranda method. - - void printError(String msg) { - System.out.println(msg); - } -} - -class Main1 extends Base { - public void foo(int i) { - if (i != 1) { - printError("error1"); - } - } -} - -class Main2 extends Main1 { - public void foo(int i) { - if (i != 2) { - printError("error2"); - } - } -} - -public class Main { - static Base sMain1; - static Base sMain2; - - static boolean sIsOptimizing = true; - static boolean sHasJIT = true; - static volatile boolean sOtherThreadStarted; - - private static void assertSingleImplementation(Class clazz, String method_name, boolean b) { - if (hasSingleImplementation(clazz, method_name) != b) { - System.out.println(clazz + "." + method_name + - " doesn't have single implementation value of " + b); - } - } - - // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked. - // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined. - // After Dummy.createMain2() which links in Main2, live testOverride() on stack - // should be deoptimized. - static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) { - if (setHasJIT) { - if (isInterpreted()) { - sHasJIT = false; - } - return; - } - - if (createMain2 && (sIsOptimizing || sHasJIT)) { - assertIsManaged(); - } - - sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); - - if (createMain2) { - // Wait for the other thread to start. - while (!sOtherThreadStarted); - // Create an Main2 instance and assign it to sMain2. - // sMain1 is kept the same. - sMain2 = Dummy.createMain2(); - // Wake up the other thread. - synchronized(Main.class) { - Main.class.notify(); - } - } else if (wait) { - // This is the other thread. - synchronized(Main.class) { - sOtherThreadStarted = true; - // Wait for Main2 to be linked and deoptimization is triggered. - try { - Main.class.wait(); - } catch (Exception e) { - } - } - } - - // There should be a deoptimization here right after Main2 is linked by - // calling Dummy.createMain2(), even though sMain1 didn't change. - // The behavior here would be different if inline-cache is used, which - // doesn't deoptimize since sMain1 still hits the type cache. - sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); - if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) { - // This method should be deoptimized right after Main2 is created. - assertIsInterpreted(); - } - - if (sMain2 != null) { - sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2); - } - } - - // Test scenarios under which CHA-based devirtualization happens, - // and class loading that overrides a method can invalidate compiled code. - public static void main(String[] args) { - System.loadLibrary(args[0]); - - if (isInterpreted()) { - sIsOptimizing = false; - } - - // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. - sMain1 = new Main1(); - - ensureJitCompiled(Main.class, "testOverride"); - testOverride(false, false, true); - - if (sHasJIT && !sIsOptimizing) { - assertSingleImplementation(Base.class, "foo", true); - assertSingleImplementation(Main1.class, "foo", true); - } else { - // Main2 is verified ahead-of-time so it's linked in already. - } - - // Create another thread that also calls sMain1.foo(). - // Try to test suspend and deopt another thread. - new Thread() { - public void run() { - testOverride(false, true, false); - } - }.start(); - - // This will create Main2 instance in the middle of testOverride(). - testOverride(true, false, false); - assertSingleImplementation(Base.class, "foo", false); - assertSingleImplementation(Main1.class, "foo", false); - } - - private static native void ensureJitCompiled(Class itf, String method_name); - private static native void assertIsInterpreted(); - private static native void assertIsManaged(); - private static native boolean isInterpreted(); - private static native boolean hasSingleImplementation(Class clazz, String method_name); -} - -// Put createMain2() in another class to avoid class loading due to verifier. -class Dummy { - static Main1 createMain2() { - return new Main2(); - } -} diff --git a/test/616-cha-proxy-method-inline/expected.txt b/test/616-cha-proxy-method-inline/expected.txt deleted file mode 100644 index 6a5618ebc..000000000 --- a/test/616-cha-proxy-method-inline/expected.txt +++ /dev/null @@ -1 +0,0 @@ -JNI_OnLoad called diff --git a/test/616-cha-proxy-method-inline/info.txt b/test/616-cha-proxy-method-inline/info.txt deleted file mode 100644 index 012685547..000000000 --- a/test/616-cha-proxy-method-inline/info.txt +++ /dev/null @@ -1 +0,0 @@ -Test for Class Hierarchy Analysis (CHA) on inlining a cross-dex proxy method. diff --git a/test/616-cha-proxy-method-inline/multidex.jpp b/test/616-cha-proxy-method-inline/multidex.jpp deleted file mode 100644 index b0d200ea3..000000000 --- a/test/616-cha-proxy-method-inline/multidex.jpp +++ /dev/null @@ -1,3 +0,0 @@ -Main: - @@com.android.jack.annotations.ForceInMainDex - class Main diff --git a/test/616-cha-proxy-method-inline/run b/test/616-cha-proxy-method-inline/run deleted file mode 100644 index d8b4f0d26..000000000 --- a/test/616-cha-proxy-method-inline/run +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2017 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. - -# Run without an app image to prevent the classes to be loaded at startup. -exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-proxy-method-inline/src-multidex/Foo.java b/test/616-cha-proxy-method-inline/src-multidex/Foo.java deleted file mode 100644 index 9deca3e64..000000000 --- a/test/616-cha-proxy-method-inline/src-multidex/Foo.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -interface Foo { - public Object bar(Object obj); -} diff --git a/test/616-cha-proxy-method-inline/src/Main.java b/test/616-cha-proxy-method-inline/src/Main.java deleted file mode 100644 index be7bc820b..000000000 --- a/test/616-cha-proxy-method-inline/src/Main.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2017 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; -import java.lang.reflect.InvocationTargetException; - -class DebugProxy implements java.lang.reflect.InvocationHandler { - private Object obj; - static Class[] interfaces = {Foo.class}; - - public static Object newInstance(Object obj) { - return java.lang.reflect.Proxy.newProxyInstance( - Foo.class.getClassLoader(), - interfaces, - new DebugProxy(obj)); - } - - private DebugProxy(Object obj) { - this.obj = obj; - } - - public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { - Object result; - if (obj == null) { - return null; - } - try { - System.out.println("before invoking method " + m.getName()); - result = m.invoke(obj, args); - } catch (InvocationTargetException e) { - throw e.getTargetException(); - } catch (Exception e) { - throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); - } finally { - System.out.println("after invoking method " + m.getName()); - } - return result; - } -} - -public class Main { - public static void call(Foo foo) { - if (foo == null) { - return; - } - foo.bar(null); - } - - public static void main(String[] args) { - System.loadLibrary(args[0]); - Foo foo = (Foo)DebugProxy.newInstance(null); - ensureJitCompiled(Main.class, "call"); - call(foo); - } - - private static native void ensureJitCompiled(Class itf, String method_name); -} -- 2.11.0