blob: 951607d2cf892a7bb647df95380fc102db2a22b0 [file] [log] [blame]
Mingyao Yang8f301e22017-02-27 16:23:51 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17class Main1 implements Base {
18}
19
20class Main2 extends Main1 {
21 public void foobar() {}
22}
23
24class Main3 implements Base {
25 public int foo(int i) {
26 if (i != 3) {
27 printError("error3");
28 }
29 return -(i + 10);
30 }
31}
32
33public class Main {
34 static Base sMain1;
35 static Base sMain2;
36 static Base sMain3;
37
38 static boolean sIsOptimizing = true;
39 static boolean sHasJIT = true;
40 static volatile boolean sOtherThreadStarted;
41
42 private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
43 if (hasSingleImplementation(clazz, method_name) != b) {
44 System.out.println(clazz + "." + method_name +
45 " doesn't have single implementation value of " + b);
46 }
47 }
48
49 static int getValue(Class<?> cls) {
50 if (cls == Main1.class || cls == Main2.class) {
51 return 1;
52 }
53 return 3;
54 }
55
56 // sMain1.foo()/sMain2.foo() will be always be Base.foo() before Main3 is loaded/linked.
57 // So sMain1.foo() can be devirtualized to Base.foo() and be inlined.
58 // After Dummy.createMain3() which links in Main3, live testImplement() on stack
59 // should be deoptimized.
60 static void testImplement(boolean createMain3, boolean wait, boolean setHasJIT) {
61 if (setHasJIT) {
62 if (isInterpreted()) {
63 sHasJIT = false;
64 }
65 return;
66 }
67
68 if (createMain3 && (sIsOptimizing || sHasJIT)) {
69 assertIsManaged();
70 }
71
72 if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
73 System.out.println("11 expected.");
74 }
75 if (sMain1.$noinline$bar() != -1) {
76 System.out.println("-1 expected.");
77 }
78 if (sMain2.foo(getValue(sMain2.getClass())) != 11) {
79 System.out.println("11 expected.");
80 }
81
82 if (createMain3) {
83 // Wait for the other thread to start.
84 while (!sOtherThreadStarted);
85 // Create an Main2 instance and assign it to sMain2.
86 // sMain1 is kept the same.
87 sMain3 = Dummy.createMain3();
88 // Wake up the other thread.
89 synchronized(Main.class) {
90 Main.class.notify();
91 }
92 } else if (wait) {
93 // This is the other thread.
94 synchronized(Main.class) {
95 sOtherThreadStarted = true;
96 // Wait for Main2 to be linked and deoptimization is triggered.
97 try {
98 Main.class.wait();
99 } catch (Exception e) {
100 }
101 }
102 }
103
104 // There should be a deoptimization here right after Main3 is linked by
105 // calling Dummy.createMain3(), even though sMain1 didn't change.
106 // The behavior here would be different if inline-cache is used, which
107 // doesn't deoptimize since sMain1 still hits the type cache.
108 if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
109 System.out.println("11 expected.");
110 }
111 if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) {
112 // This method should be deoptimized right after Main3 is created.
113 assertIsInterpreted();
114 }
115
116 if (sMain3 != null) {
117 if (sMain3.foo(getValue(sMain3.getClass())) != -13) {
118 System.out.println("-13 expected.");
119 }
120 }
121 }
122
123 // Test scenarios under which CHA-based devirtualization happens,
124 // and class loading that implements a method can invalidate compiled code.
125 public static void main(String[] args) {
126 System.loadLibrary(args[0]);
127
128 if (isInterpreted()) {
129 sIsOptimizing = false;
130 }
131
132 // sMain1 is an instance of Main1.
133 // sMain2 is an instance of Main2.
134 // Neither Main1 nor Main2 override default method Base.foo().
135 // Main3 hasn't bee loaded yet.
136 sMain1 = new Main1();
137 sMain2 = new Main2();
138
139 ensureJitCompiled(Main.class, "testImplement");
140 testImplement(false, false, true);
141
142 if (sHasJIT && !sIsOptimizing) {
143 assertSingleImplementation(Base.class, "foo", true);
144 assertSingleImplementation(Main1.class, "foo", true);
145 } else {
146 // Main3 is verified ahead-of-time so it's linked in already.
147 }
148
149 // Create another thread that also calls sMain1.foo().
150 // Try to test suspend and deopt another thread.
151 new Thread() {
152 public void run() {
153 testImplement(false, true, false);
154 }
155 }.start();
156
157 // This will create Main3 instance in the middle of testImplement().
158 testImplement(true, false, false);
159 assertSingleImplementation(Base.class, "foo", false);
160 assertSingleImplementation(Main1.class, "foo", true);
161 assertSingleImplementation(sMain3.getClass(), "foo", true);
162 }
163
164 private static native void ensureJitCompiled(Class<?> itf, String method_name);
165 private static native void assertIsInterpreted();
166 private static native void assertIsManaged();
167 private static native boolean isInterpreted();
168 private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
169}
170
171// Put createMain3() in another class to avoid class loading due to verifier.
172class Dummy {
173 static Base createMain3() {
174 return new Main3();
175 }
176}