1 /*
2  * Copyright (C) 2016 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 import java.lang.reflect.Method;
18 import java.util.Arrays;
19 import java.util.Comparator;
20 import java.util.HashMap;
21 
22 class DummyObject {
23     public static boolean sHashCodeInvoked = false;
24     private int i;
25 
DummyObject(int i)26     public DummyObject(int i) {
27         this.i = i;
28     }
29 
equals(Object obj)30     public boolean equals(Object obj) {
31         return (obj instanceof DummyObject) && (i == ((DummyObject)obj).i);
32     }
33 
hashCode()34     public int hashCode() {
35         sHashCodeInvoked = true;
36         Main.assertIsManaged();
37         Main.deoptimizeAll();
38         Main.assertIsInterpreted();
39         return i % 64;
40     }
41 }
42 
43 public class Main {
44     static boolean sFlag = false;
45 
deoptimizeAll()46     public static native void deoptimizeAll();
undeoptimizeAll()47     public static native void undeoptimizeAll();
assertIsInterpreted()48     public static native void assertIsInterpreted();
assertIsManaged()49     public static native void assertIsManaged();
assertCallerIsInterpreted()50     public static native void assertCallerIsInterpreted();
disableStackFrameAsserts()51     public static native void disableStackFrameAsserts();
hasJit()52     public static native boolean hasJit();
ensureJitCompiled(Class<?> itf, String method_name)53     private static native void ensureJitCompiled(Class<?> itf, String method_name);
54 
execute(Runnable runnable)55     public static void execute(Runnable runnable) throws Exception {
56       Thread t = new Thread(runnable);
57       t.start();
58       t.join();
59     }
60 
ensureAllJitCompiled()61     public static void ensureAllJitCompiled() {
62         ensureJitCompiled(HashMap.class, "hash");
63         ensureJitCompiled(Main.class, "$noinline$run1");
64         ensureJitCompiled(Main.class, "$noinline$run2");
65         ensureJitCompiled(Main.class, "$noinline$run3A");
66         ensureJitCompiled(Main.class, "$noinline$run3B");
67         ensureJitCompiled(DummyObject.class, "hashCode");
68     }
69 
main(String[] args)70     public static void main(String[] args) throws Exception {
71         System.loadLibrary(args[0]);
72         // Only test stack frames in compiled mode.
73         if (!hasJit()) {
74           disableStackFrameAsserts();
75         }
76 
77         ensureAllJitCompiled();
78 
79         final HashMap<DummyObject, Long> map = new HashMap<DummyObject, Long>();
80 
81         // Single-frame deoptimization that covers partial fragment.
82         execute(new Runnable() {
83             public void run() {
84                 ensureJitCompiled(this.getClass(), "runInternal");
85                 runInternal();
86             }
87 
88             public void runInternal() {
89                 int[] arr = new int[3];
90                 assertIsManaged();
91                 int res = $noinline$run1(arr);
92                 assertIsManaged();  // Only single frame is deoptimized.
93                 if (res != 79) {
94                     System.out.println("Failure 1!");
95                     System.exit(0);
96                 }
97             }
98         });
99 
100         // Single-frame deoptimization that covers a full fragment.
101         execute(new Runnable() {
102             public void run() {
103                 ensureJitCompiled(this.getClass(), "runInternal");
104                 runInternal();
105             }
106 
107             public void runInternal() {
108                 try {
109                     int[] arr = new int[3];
110                     assertIsManaged();
111                     // Use reflection to call $noinline$run2 so that it does
112                     // full-fragment deoptimization since that is an upcall.
113                     Class<?> cls = Class.forName("Main");
114                     Method method = cls.getDeclaredMethod("$noinline$run2", int[].class);
115                     double res = (double)method.invoke(Main.class, arr);
116                     assertIsManaged();  // Only single frame is deoptimized.
117                     if (res != 79.3d) {
118                         System.out.println("Failure 2!");
119                         System.exit(0);
120                     }
121                 } catch (Exception e) {
122                     e.printStackTrace(System.out);
123                 }
124             }
125         });
126 
127         // Full-fragment deoptimization.
128         execute(new Runnable() {
129             public void run() {
130                 ensureJitCompiled(this.getClass(), "runInternal");
131                 runInternal();
132             }
133 
134             public void runInternal() {
135                 assertIsManaged();
136                 float res = $noinline$run3B();
137                 assertIsInterpreted();  // Every deoptimizeable method is deoptimized.
138                 if (res != 0.034f) {
139                     System.out.println("Failure 3!");
140                     System.exit(0);
141                 }
142             }
143         });
144 
145         undeoptimizeAll();  // Make compiled code useable again.
146         ensureAllJitCompiled();
147 
148         // Partial-fragment deoptimization.
149         execute(new Runnable() {
150             public void run() {
151                 ensureJitCompiled(this.getClass(), "runInternal");
152                 ensureJitCompiled(HashMap.class, "hash");
153                 runInternal();
154             }
155 
156             public void runInternal() {
157                 try {
158                     assertIsManaged();
159                     map.put(new DummyObject(10), Long.valueOf(100));
160                     assertIsInterpreted();  // Every deoptimizeable method is deoptimized.
161                 } catch (Exception e) {
162                     e.printStackTrace(System.out);
163                 }
164             }
165         });
166 
167         undeoptimizeAll();  // Make compiled code useable again.
168         ensureAllJitCompiled();
169 
170         if (!DummyObject.sHashCodeInvoked) {
171             System.out.println("hashCode() method not invoked!");
172         }
173         if (map.get(new DummyObject(10)) != 100) {
174             System.out.println("Wrong hashmap value!");
175         }
176         System.out.println("Finishing");
177     }
178 
$noinline$run1(int[] arr)179     public static int $noinline$run1(int[] arr) {
180         assertIsManaged();
181         // Prevent inlining.
182         if (sFlag) {
183             throw new Error();
184         }
185         boolean caught = false;
186         // BCE will use deoptimization for the code below.
187         try {
188             arr[0] = 1;
189             arr[1] = 1;
190             arr[2] = 1;
191             // This causes AIOOBE and triggers deoptimization from compiled code.
192             arr[3] = 1;
193         } catch (ArrayIndexOutOfBoundsException e) {
194             assertIsInterpreted(); // Single-frame deoptimization triggered.
195             caught = true;
196         }
197         if (!caught) {
198             System.out.println("Expected exception");
199         }
200         assertIsInterpreted();
201         return 79;
202     }
203 
$noinline$run2(int[] arr)204     public static double $noinline$run2(int[] arr) {
205         assertIsManaged();
206         // Prevent inlining.
207         if (sFlag) {
208             throw new Error();
209         }
210         boolean caught = false;
211         // BCE will use deoptimization for the code below.
212         try {
213             arr[0] = 1;
214             arr[1] = 1;
215             arr[2] = 1;
216             // This causes AIOOBE and triggers deoptimization from compiled code.
217             arr[3] = 1;
218         } catch (ArrayIndexOutOfBoundsException e) {
219             assertIsInterpreted();  // Single-frame deoptimization triggered.
220             caught = true;
221         }
222         if (!caught) {
223             System.out.println("Expected exception");
224         }
225         assertIsInterpreted();
226         return 79.3d;
227     }
228 
$noinline$run3A()229     public static float $noinline$run3A() {
230         assertIsManaged();
231         // Prevent inlining.
232         if (sFlag) {
233             throw new Error();
234         }
235         // Deoptimize callers.
236         deoptimizeAll();
237         assertIsInterpreted();
238         assertCallerIsInterpreted();  // $noinline$run3B is deoptimizeable.
239         return 0.034f;
240     }
241 
$noinline$run3B()242     public static float $noinline$run3B() {
243         assertIsManaged();
244         // Prevent inlining.
245         if (sFlag) {
246             throw new Error();
247         }
248         float res = $noinline$run3A();
249         assertIsInterpreted();
250         return res;
251     }
252 }
253