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