blob: c55ed6eeef2f309148f354dedb56ca0539f5b7c0 [file] [log] [blame]
Nicolas Geoffray18ea1c92017-03-27 08:00:18 +00001/*
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
17interface Base {
18 void foo(int i);
19 void $noinline$bar();
20}
21
22class Main1 implements Base {
23 public void foo(int i) {
24 if (i != 1) {
25 printError("error1");
26 }
27 }
28
29 // Test rewriting invoke-interface into invoke-virtual when inlining fails.
30 public void $noinline$bar() {
31 System.out.print("");
32 System.out.print("");
33 System.out.print("");
34 System.out.print("");
35 System.out.print("");
36 System.out.print("");
37 System.out.print("");
38 System.out.print("");
39 }
40
41 void printError(String msg) {
42 System.out.println(msg);
43 }
44}
45
46class Main2 extends Main1 {
47 public void foo(int i) {
48 if (i != 2) {
49 printError("error2");
50 }
51 }
52}
53
54public class Main {
55 static Base sMain1;
56 static Base sMain2;
57
58 static boolean sIsOptimizing = true;
59 static boolean sHasJIT = true;
60 static volatile boolean sOtherThreadStarted;
61
62 private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
63 if (hasSingleImplementation(clazz, method_name) != b) {
64 System.out.println(clazz + "." + method_name +
65 " doesn't have single implementation value of " + b);
66 }
67 }
68
69 // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
70 // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
Orion Hodson2731eb42020-07-24 12:10:12 +010071 // After Helper.createMain2() which links in Main2, live testImplement() on stack
Nicolas Geoffray18ea1c92017-03-27 08:00:18 +000072 // should be deoptimized.
73 static void testImplement(boolean createMain2, boolean wait, boolean setHasJIT) {
74 if (setHasJIT) {
75 if (isInterpreted()) {
76 sHasJIT = false;
77 }
78 return;
79 }
80
81 if (createMain2 && (sIsOptimizing || sHasJIT)) {
82 assertIsManaged();
83 }
84
85 sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
86 sMain1.$noinline$bar();
87
88 if (createMain2) {
89 // Wait for the other thread to start.
90 while (!sOtherThreadStarted);
91 // Create an Main2 instance and assign it to sMain2.
92 // sMain1 is kept the same.
Orion Hodson2731eb42020-07-24 12:10:12 +010093 sMain2 = Helper.createMain2();
Nicolas Geoffray18ea1c92017-03-27 08:00:18 +000094 // Wake up the other thread.
95 synchronized(Main.class) {
96 Main.class.notify();
97 }
98 } else if (wait) {
99 // This is the other thread.
100 synchronized(Main.class) {
101 sOtherThreadStarted = true;
102 // Wait for Main2 to be linked and deoptimization is triggered.
103 try {
104 Main.class.wait();
105 } catch (Exception e) {
106 }
107 }
108 }
109
110 // There should be a deoptimization here right after Main2 is linked by
Orion Hodson2731eb42020-07-24 12:10:12 +0100111 // calling Helper.createMain2(), even though sMain1 didn't change.
Nicolas Geoffray18ea1c92017-03-27 08:00:18 +0000112 // The behavior here would be different if inline-cache is used, which
113 // doesn't deoptimize since sMain1 still hits the type cache.
114 sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
115 if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
116 // This method should be deoptimized right after Main2 is created.
117 assertIsInterpreted();
118 }
119
120 if (sMain2 != null) {
121 sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
122 }
123 }
124
125 // Test scenarios under which CHA-based devirtualization happens,
126 // and class loading that overrides a method can invalidate compiled code.
127 public static void main(String[] args) {
128 System.loadLibrary(args[0]);
129
130 if (isInterpreted()) {
131 sIsOptimizing = false;
132 }
133
134 // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
135 sMain1 = new Main1();
136
137 ensureJitCompiled(Main.class, "testImplement");
138 testImplement(false, false, true);
139
140 if (sHasJIT && !sIsOptimizing) {
141 assertSingleImplementation(Base.class, "foo", true);
142 assertSingleImplementation(Main1.class, "foo", true);
143 } else {
144 // Main2 is verified ahead-of-time so it's linked in already.
145 }
146
147 // Create another thread that also calls sMain1.foo().
148 // Try to test suspend and deopt another thread.
149 new Thread() {
150 public void run() {
151 testImplement(false, true, false);
152 }
153 }.start();
154
155 // This will create Main2 instance in the middle of testImplement().
156 testImplement(true, false, false);
157 assertSingleImplementation(Base.class, "foo", false);
158 assertSingleImplementation(Main1.class, "foo", false);
159 }
160
161 private static native void ensureJitCompiled(Class<?> itf, String method_name);
162 private static native void assertIsInterpreted();
163 private static native void assertIsManaged();
164 private static native boolean isInterpreted();
165 private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
166}
167
168// Put createMain2() in another class to avoid class loading due to verifier.
Orion Hodson2731eb42020-07-24 12:10:12 +0100169class Helper {
Nicolas Geoffray18ea1c92017-03-27 08:00:18 +0000170 static Main1 createMain2() {
171 return new Main2();
172 }
173}