xref: /aosp_15_r20/art/test/616-cha/src/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2016 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker class Main1 {
getName()18*795d594fSAndroid Build Coastguard Worker     String getName() {
19*795d594fSAndroid Build Coastguard Worker         return "Main1";
20*795d594fSAndroid Build Coastguard Worker     }
21*795d594fSAndroid Build Coastguard Worker 
printError(String msg)22*795d594fSAndroid Build Coastguard Worker     void printError(String msg) {
23*795d594fSAndroid Build Coastguard Worker         System.out.println(msg);
24*795d594fSAndroid Build Coastguard Worker     }
25*795d594fSAndroid Build Coastguard Worker 
foo(int i)26*795d594fSAndroid Build Coastguard Worker     void foo(int i) {
27*795d594fSAndroid Build Coastguard Worker         if (i != 1) {
28*795d594fSAndroid Build Coastguard Worker             printError("error1");
29*795d594fSAndroid Build Coastguard Worker         }
30*795d594fSAndroid Build Coastguard Worker     }
31*795d594fSAndroid Build Coastguard Worker 
getValue1()32*795d594fSAndroid Build Coastguard Worker     int getValue1() {
33*795d594fSAndroid Build Coastguard Worker         return 1;
34*795d594fSAndroid Build Coastguard Worker     }
getValue2()35*795d594fSAndroid Build Coastguard Worker     int getValue2() {
36*795d594fSAndroid Build Coastguard Worker         return 2;
37*795d594fSAndroid Build Coastguard Worker     }
getValue3()38*795d594fSAndroid Build Coastguard Worker     int getValue3() {
39*795d594fSAndroid Build Coastguard Worker         return 3;
40*795d594fSAndroid Build Coastguard Worker     }
getValue4()41*795d594fSAndroid Build Coastguard Worker     int getValue4() {
42*795d594fSAndroid Build Coastguard Worker         return 4;
43*795d594fSAndroid Build Coastguard Worker     }
getValue5()44*795d594fSAndroid Build Coastguard Worker     int getValue5() {
45*795d594fSAndroid Build Coastguard Worker         return 5;
46*795d594fSAndroid Build Coastguard Worker     }
getValue6()47*795d594fSAndroid Build Coastguard Worker     int getValue6() {
48*795d594fSAndroid Build Coastguard Worker         return 6;
49*795d594fSAndroid Build Coastguard Worker     }
50*795d594fSAndroid Build Coastguard Worker }
51*795d594fSAndroid Build Coastguard Worker 
52*795d594fSAndroid Build Coastguard Worker class Main2 extends Main1 {
getName()53*795d594fSAndroid Build Coastguard Worker     String getName() {
54*795d594fSAndroid Build Coastguard Worker         return "Main2";
55*795d594fSAndroid Build Coastguard Worker     }
56*795d594fSAndroid Build Coastguard Worker 
foo(int i)57*795d594fSAndroid Build Coastguard Worker     void foo(int i) {
58*795d594fSAndroid Build Coastguard Worker         if (i != 2) {
59*795d594fSAndroid Build Coastguard Worker             printError("error2");
60*795d594fSAndroid Build Coastguard Worker         }
61*795d594fSAndroid Build Coastguard Worker     }
62*795d594fSAndroid Build Coastguard Worker }
63*795d594fSAndroid Build Coastguard Worker 
64*795d594fSAndroid Build Coastguard Worker class Main3 extends Main1 {
getName()65*795d594fSAndroid Build Coastguard Worker     String getName() {
66*795d594fSAndroid Build Coastguard Worker         return "Main3";
67*795d594fSAndroid Build Coastguard Worker     }
68*795d594fSAndroid Build Coastguard Worker }
69*795d594fSAndroid Build Coastguard Worker 
70*795d594fSAndroid Build Coastguard Worker public class Main {
71*795d594fSAndroid Build Coastguard Worker     static Main1 sMain1;
72*795d594fSAndroid Build Coastguard Worker     static Main1 sMain2;
73*795d594fSAndroid Build Coastguard Worker 
74*795d594fSAndroid Build Coastguard Worker     static boolean sIsOptimizing = true;
75*795d594fSAndroid Build Coastguard Worker     static boolean sHasJIT = true;
76*795d594fSAndroid Build Coastguard Worker     static volatile boolean sOtherThreadStarted;
77*795d594fSAndroid Build Coastguard Worker 
78*795d594fSAndroid Build Coastguard Worker     // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
79*795d594fSAndroid Build Coastguard Worker     // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
80*795d594fSAndroid Build Coastguard Worker     // After Helper.createMain2() which links in Main2, live testOverride() on stack
81*795d594fSAndroid Build Coastguard Worker     // should be deoptimized.
testOverride(boolean createMain2, boolean wait, boolean setHasJIT)82*795d594fSAndroid Build Coastguard Worker     static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
83*795d594fSAndroid Build Coastguard Worker         if (setHasJIT) {
84*795d594fSAndroid Build Coastguard Worker             if (isInterpreted()) {
85*795d594fSAndroid Build Coastguard Worker                 sHasJIT = false;
86*795d594fSAndroid Build Coastguard Worker             }
87*795d594fSAndroid Build Coastguard Worker             return;
88*795d594fSAndroid Build Coastguard Worker         }
89*795d594fSAndroid Build Coastguard Worker 
90*795d594fSAndroid Build Coastguard Worker         if (createMain2 && (sIsOptimizing || sHasJIT)) {
91*795d594fSAndroid Build Coastguard Worker             assertIsManaged();
92*795d594fSAndroid Build Coastguard Worker         }
93*795d594fSAndroid Build Coastguard Worker 
94*795d594fSAndroid Build Coastguard Worker         sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
95*795d594fSAndroid Build Coastguard Worker 
96*795d594fSAndroid Build Coastguard Worker         if (createMain2) {
97*795d594fSAndroid Build Coastguard Worker             // Wait for the other thread to start.
98*795d594fSAndroid Build Coastguard Worker             while (!sOtherThreadStarted);
99*795d594fSAndroid Build Coastguard Worker             // Create an Main2 instance and assign it to sMain2.
100*795d594fSAndroid Build Coastguard Worker             // sMain1 is kept the same.
101*795d594fSAndroid Build Coastguard Worker             sMain2 = Helper.createMain2();
102*795d594fSAndroid Build Coastguard Worker             // Wake up the other thread.
103*795d594fSAndroid Build Coastguard Worker             synchronized(Main.class) {
104*795d594fSAndroid Build Coastguard Worker                 Main.class.notify();
105*795d594fSAndroid Build Coastguard Worker             }
106*795d594fSAndroid Build Coastguard Worker         } else if (wait) {
107*795d594fSAndroid Build Coastguard Worker             // This is the other thread.
108*795d594fSAndroid Build Coastguard Worker             synchronized(Main.class) {
109*795d594fSAndroid Build Coastguard Worker                 sOtherThreadStarted = true;
110*795d594fSAndroid Build Coastguard Worker                 // Wait for Main2 to be linked and deoptimization is triggered.
111*795d594fSAndroid Build Coastguard Worker                 try {
112*795d594fSAndroid Build Coastguard Worker                     Main.class.wait();
113*795d594fSAndroid Build Coastguard Worker                 } catch (Exception e) {
114*795d594fSAndroid Build Coastguard Worker                 }
115*795d594fSAndroid Build Coastguard Worker             }
116*795d594fSAndroid Build Coastguard Worker         }
117*795d594fSAndroid Build Coastguard Worker 
118*795d594fSAndroid Build Coastguard Worker         // There should be a deoptimization here right after Main2 is linked by
119*795d594fSAndroid Build Coastguard Worker         // calling Helper.createMain2(), even though sMain1 didn't change.
120*795d594fSAndroid Build Coastguard Worker         // The behavior here would be different if inline-cache is used, which
121*795d594fSAndroid Build Coastguard Worker         // doesn't deoptimize since sMain1 still hits the type cache.
122*795d594fSAndroid Build Coastguard Worker         sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
123*795d594fSAndroid Build Coastguard Worker         if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
124*795d594fSAndroid Build Coastguard Worker             // This method should be deoptimized right after Main2 is created.
125*795d594fSAndroid Build Coastguard Worker             assertIsInterpreted();
126*795d594fSAndroid Build Coastguard Worker         }
127*795d594fSAndroid Build Coastguard Worker 
128*795d594fSAndroid Build Coastguard Worker         if (sMain2 != null) {
129*795d594fSAndroid Build Coastguard Worker             sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
130*795d594fSAndroid Build Coastguard Worker         }
131*795d594fSAndroid Build Coastguard Worker     }
132*795d594fSAndroid Build Coastguard Worker 
133*795d594fSAndroid Build Coastguard Worker     static Main1[] sArray;
134*795d594fSAndroid Build Coastguard Worker 
calcValue(Main1 m)135*795d594fSAndroid Build Coastguard Worker     static long calcValue(Main1 m) {
136*795d594fSAndroid Build Coastguard Worker         return m.getValue1()
137*795d594fSAndroid Build Coastguard Worker                 + m.getValue2() * 2
138*795d594fSAndroid Build Coastguard Worker                 + m.getValue3() * 3
139*795d594fSAndroid Build Coastguard Worker                 + m.getValue4() * 4
140*795d594fSAndroid Build Coastguard Worker                 + m.getValue5() * 5
141*795d594fSAndroid Build Coastguard Worker                 + m.getValue6() * 6;
142*795d594fSAndroid Build Coastguard Worker     }
143*795d594fSAndroid Build Coastguard Worker 
testNoOverrideLoop(int count)144*795d594fSAndroid Build Coastguard Worker     static long testNoOverrideLoop(int count) {
145*795d594fSAndroid Build Coastguard Worker         long sum = 0;
146*795d594fSAndroid Build Coastguard Worker         for (int i=0; i<count; i++) {
147*795d594fSAndroid Build Coastguard Worker             sum += calcValue(sArray[0]);
148*795d594fSAndroid Build Coastguard Worker             sum += calcValue(sArray[1]);
149*795d594fSAndroid Build Coastguard Worker             sum += calcValue(sArray[2]);
150*795d594fSAndroid Build Coastguard Worker         }
151*795d594fSAndroid Build Coastguard Worker         return sum;
152*795d594fSAndroid Build Coastguard Worker     }
153*795d594fSAndroid Build Coastguard Worker 
testNoOverride()154*795d594fSAndroid Build Coastguard Worker     static void testNoOverride() {
155*795d594fSAndroid Build Coastguard Worker         sArray = new Main1[3];
156*795d594fSAndroid Build Coastguard Worker         sArray[0] = new Main1();
157*795d594fSAndroid Build Coastguard Worker         sArray[1] = Helper.createMain2();
158*795d594fSAndroid Build Coastguard Worker         sArray[2] = Helper.createMain3();
159*795d594fSAndroid Build Coastguard Worker         long sum = 0;
160*795d594fSAndroid Build Coastguard Worker         // Loop enough to get methods JITed.
161*795d594fSAndroid Build Coastguard Worker         for (int i=0; i<100; i++) {
162*795d594fSAndroid Build Coastguard Worker             testNoOverrideLoop(1);
163*795d594fSAndroid Build Coastguard Worker         }
164*795d594fSAndroid Build Coastguard Worker         ensureJitCompiled(Main.class, "testNoOverrideLoop");
165*795d594fSAndroid Build Coastguard Worker         ensureJitCompiled(Main.class, "calcValue");
166*795d594fSAndroid Build Coastguard Worker 
167*795d594fSAndroid Build Coastguard Worker         long t1 = System.currentTimeMillis();
168*795d594fSAndroid Build Coastguard Worker         sum = testNoOverrideLoop(100000);
169*795d594fSAndroid Build Coastguard Worker         long t2 = System.currentTimeMillis();
170*795d594fSAndroid Build Coastguard Worker         if (sum != 27300000L) {
171*795d594fSAndroid Build Coastguard Worker             System.out.println("Unexpected result.");
172*795d594fSAndroid Build Coastguard Worker         }
173*795d594fSAndroid Build Coastguard Worker     }
174*795d594fSAndroid Build Coastguard Worker 
assertSingleImplementation(Class<?> clazz, String method_name, boolean b)175*795d594fSAndroid Build Coastguard Worker     private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
176*795d594fSAndroid Build Coastguard Worker         if (hasSingleImplementation(clazz, method_name) != b) {
177*795d594fSAndroid Build Coastguard Worker             System.out.println(clazz + "." + method_name +
178*795d594fSAndroid Build Coastguard Worker                     " doesn't have single implementation value of " + b);
179*795d594fSAndroid Build Coastguard Worker         }
180*795d594fSAndroid Build Coastguard Worker     }
181*795d594fSAndroid Build Coastguard Worker 
182*795d594fSAndroid Build Coastguard Worker     // Test scenarios under which CHA-based devirtualization happens,
183*795d594fSAndroid Build Coastguard Worker     // and class loading that overrides a method can invalidate compiled code.
184*795d594fSAndroid Build Coastguard Worker     // Also test pure non-overriding case, which is more for checking generated
185*795d594fSAndroid Build Coastguard Worker     // code form.
main(String[] args)186*795d594fSAndroid Build Coastguard Worker     public static void main(String[] args) {
187*795d594fSAndroid Build Coastguard Worker         System.loadLibrary(args[0]);
188*795d594fSAndroid Build Coastguard Worker 
189*795d594fSAndroid Build Coastguard Worker         // CHeck some boot-image methods.
190*795d594fSAndroid Build Coastguard Worker 
191*795d594fSAndroid Build Coastguard Worker         // We would want to have this, but currently setting single-implementation in the boot image
192*795d594fSAndroid Build Coastguard Worker         // does not work well with app images. b/34193647
193*795d594fSAndroid Build Coastguard Worker         final boolean ARRAYLIST_SIZE_EXPECTED = false;
194*795d594fSAndroid Build Coastguard Worker         assertSingleImplementation(java.util.ArrayList.class, "size", ARRAYLIST_SIZE_EXPECTED);
195*795d594fSAndroid Build Coastguard Worker 
196*795d594fSAndroid Build Coastguard Worker         // java.util.LinkedHashMap overrides get().
197*795d594fSAndroid Build Coastguard Worker         assertSingleImplementation(java.util.HashMap.class, "get", false);
198*795d594fSAndroid Build Coastguard Worker 
199*795d594fSAndroid Build Coastguard Worker         // We don't set single-implementation modifier bit for final classes or methods
200*795d594fSAndroid Build Coastguard Worker         // since we can devirtualize without CHA for those cases. However hasSingleImplementation()
201*795d594fSAndroid Build Coastguard Worker         // should return true for those cases.
202*795d594fSAndroid Build Coastguard Worker         assertSingleImplementation(java.lang.String.class, "charAt", true);
203*795d594fSAndroid Build Coastguard Worker         assertSingleImplementation(java.lang.Thread.class, "join", true);
204*795d594fSAndroid Build Coastguard Worker 
205*795d594fSAndroid Build Coastguard Worker         if (isInterpreted()) {
206*795d594fSAndroid Build Coastguard Worker             sIsOptimizing = false;
207*795d594fSAndroid Build Coastguard Worker         }
208*795d594fSAndroid Build Coastguard Worker 
209*795d594fSAndroid Build Coastguard Worker         // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
210*795d594fSAndroid Build Coastguard Worker         sMain1 = new Main1();
211*795d594fSAndroid Build Coastguard Worker 
212*795d594fSAndroid Build Coastguard Worker         ensureJitCompiled(Main.class, "testOverride");
213*795d594fSAndroid Build Coastguard Worker         testOverride(false, false, true);
214*795d594fSAndroid Build Coastguard Worker 
215*795d594fSAndroid Build Coastguard Worker         if (sHasJIT && !sIsOptimizing) {
216*795d594fSAndroid Build Coastguard Worker             assertSingleImplementation(Main1.class, "foo", true);
217*795d594fSAndroid Build Coastguard Worker         } else {
218*795d594fSAndroid Build Coastguard Worker             // Main2 is verified ahead-of-time so it's linked in already.
219*795d594fSAndroid Build Coastguard Worker         }
220*795d594fSAndroid Build Coastguard Worker         assertSingleImplementation(Main1.class, "getValue1", true);
221*795d594fSAndroid Build Coastguard Worker 
222*795d594fSAndroid Build Coastguard Worker         // Create another thread that also calls sMain1.foo().
223*795d594fSAndroid Build Coastguard Worker         // Try to test suspend and deopt another thread.
224*795d594fSAndroid Build Coastguard Worker         new Thread() {
225*795d594fSAndroid Build Coastguard Worker             public void run() {
226*795d594fSAndroid Build Coastguard Worker                 testOverride(false, true, false);
227*795d594fSAndroid Build Coastguard Worker             }
228*795d594fSAndroid Build Coastguard Worker         }.start();
229*795d594fSAndroid Build Coastguard Worker 
230*795d594fSAndroid Build Coastguard Worker         // This will create Main2 instance in the middle of testOverride().
231*795d594fSAndroid Build Coastguard Worker         testOverride(true, false, false);
232*795d594fSAndroid Build Coastguard Worker         assertSingleImplementation(Main1.class, "foo", false);
233*795d594fSAndroid Build Coastguard Worker         assertSingleImplementation(Main1.class, "getValue1", true);
234*795d594fSAndroid Build Coastguard Worker 
235*795d594fSAndroid Build Coastguard Worker         testNoOverride();
236*795d594fSAndroid Build Coastguard Worker     }
237*795d594fSAndroid Build Coastguard Worker 
ensureJitCompiled(Class<?> itf, String method_name)238*795d594fSAndroid Build Coastguard Worker     private static native void ensureJitCompiled(Class<?> itf, String method_name);
assertIsInterpreted()239*795d594fSAndroid Build Coastguard Worker     private static native void assertIsInterpreted();
assertIsManaged()240*795d594fSAndroid Build Coastguard Worker     private static native void assertIsManaged();
isInterpreted()241*795d594fSAndroid Build Coastguard Worker     private static native boolean isInterpreted();
hasSingleImplementation(Class<?> clazz, String method_name)242*795d594fSAndroid Build Coastguard Worker     private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
243*795d594fSAndroid Build Coastguard Worker }
244*795d594fSAndroid Build Coastguard Worker 
245*795d594fSAndroid Build Coastguard Worker // Put createMain2() in another class to avoid class loading due to verifier.
246*795d594fSAndroid Build Coastguard Worker class Helper {
createMain2()247*795d594fSAndroid Build Coastguard Worker     static Main1 createMain2() {
248*795d594fSAndroid Build Coastguard Worker         return new Main2();
249*795d594fSAndroid Build Coastguard Worker     }
createMain3()250*795d594fSAndroid Build Coastguard Worker     static Main1 createMain3() {
251*795d594fSAndroid Build Coastguard Worker         return new Main3();
252*795d594fSAndroid Build Coastguard Worker     }
253*795d594fSAndroid Build Coastguard Worker }
254