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 SampleObject { 23 public static boolean sHashCodeInvoked = false; 24 private int i; 25 SampleObject(int i)26 public SampleObject(int i) { 27 this.i = i; 28 } 29 equals(Object obj)30 public boolean equals(Object obj) { 31 return (obj instanceof SampleObject) && (i == ((SampleObject)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(SampleObject.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 // Just declare a new int array so that the int arrays are resolved properly when JITing. 78 int[] tmp = new int[3]; 79 ensureAllJitCompiled(); 80 81 final HashMap<SampleObject, Long> map = new HashMap<SampleObject, Long>(); 82 83 // Single-frame deoptimization that covers partial fragment. 84 execute(new Runnable() { 85 public void run() { 86 ensureJitCompiled(this.getClass(), "runInternal"); 87 runInternal(); 88 } 89 90 public void runInternal() { 91 int[] arr = new int[3]; 92 assertIsManaged(); 93 int res = $noinline$run1(arr); 94 assertIsManaged(); // Only single frame is deoptimized. 95 if (res != 79) { 96 System.out.println("Failure 1!"); 97 System.exit(0); 98 } 99 } 100 }); 101 102 // Single-frame deoptimization that covers a full fragment. 103 execute(new Runnable() { 104 public void run() { 105 ensureJitCompiled(this.getClass(), "runInternal"); 106 runInternal(); 107 } 108 109 public void runInternal() { 110 try { 111 int[] arr = new int[3]; 112 assertIsManaged(); 113 // Use reflection to call $noinline$run2 so that it does 114 // full-fragment deoptimization since that is an upcall. 115 Class<?> cls = Class.forName("Main"); 116 Method method = cls.getDeclaredMethod("$noinline$run2", int[].class); 117 double res = (double)method.invoke(Main.class, arr); 118 assertIsManaged(); // Only single frame is deoptimized. 119 if (res != 79.3d) { 120 System.out.println("Failure 2!"); 121 System.exit(0); 122 } 123 } catch (Exception e) { 124 e.printStackTrace(System.out); 125 } 126 } 127 }); 128 129 // Full-fragment deoptimization. 130 execute(new Runnable() { 131 public void run() { 132 ensureJitCompiled(this.getClass(), "runInternal"); 133 runInternal(); 134 } 135 136 public void runInternal() { 137 assertIsManaged(); 138 float res = $noinline$run3B(); 139 assertIsInterpreted(); // Every deoptimizeable method is deoptimized. 140 if (res != 0.034f) { 141 System.out.println("Failure 3!"); 142 System.exit(0); 143 } 144 } 145 }); 146 147 undeoptimizeAll(); // Make compiled code useable again. 148 ensureAllJitCompiled(); 149 150 // Partial-fragment deoptimization. 151 execute(new Runnable() { 152 public void run() { 153 ensureJitCompiled(this.getClass(), "runInternal"); 154 ensureJitCompiled(HashMap.class, "hash"); 155 runInternal(); 156 } 157 158 public void runInternal() { 159 try { 160 assertIsManaged(); 161 map.put(new SampleObject(10), Long.valueOf(100)); 162 assertIsInterpreted(); // Every deoptimizeable method is deoptimized. 163 } catch (Exception e) { 164 e.printStackTrace(System.out); 165 } 166 } 167 }); 168 169 undeoptimizeAll(); // Make compiled code useable again. 170 ensureAllJitCompiled(); 171 172 if (!SampleObject.sHashCodeInvoked) { 173 System.out.println("hashCode() method not invoked!"); 174 } 175 if (map.get(new SampleObject(10)) != 100) { 176 System.out.println("Wrong hashmap value!"); 177 } 178 System.out.println("Finishing"); 179 } 180 $noinline$run1(int[] arr)181 public static int $noinline$run1(int[] arr) { 182 assertIsManaged(); 183 // Prevent inlining. 184 if (sFlag) { 185 throw new Error(); 186 } 187 boolean caught = false; 188 // BCE will use deoptimization for the code below. 189 try { 190 arr[0] = 1; 191 arr[1] = 1; 192 arr[2] = 1; 193 // This causes AIOOBE and triggers deoptimization from compiled code. 194 arr[3] = 1; 195 } catch (ArrayIndexOutOfBoundsException e) { 196 assertIsInterpreted(); // Single-frame deoptimization triggered. 197 caught = true; 198 } 199 if (!caught) { 200 System.out.println("Expected exception"); 201 } 202 assertIsInterpreted(); 203 return 79; 204 } 205 $noinline$run2(int[] arr)206 public static double $noinline$run2(int[] arr) { 207 assertIsManaged(); 208 // Prevent inlining. 209 if (sFlag) { 210 throw new Error(); 211 } 212 boolean caught = false; 213 // BCE will use deoptimization for the code below. 214 try { 215 arr[0] = 1; 216 arr[1] = 1; 217 arr[2] = 1; 218 // This causes AIOOBE and triggers deoptimization from compiled code. 219 arr[3] = 1; 220 } catch (ArrayIndexOutOfBoundsException e) { 221 assertIsInterpreted(); // Single-frame deoptimization triggered. 222 caught = true; 223 } 224 if (!caught) { 225 System.out.println("Expected exception"); 226 } 227 assertIsInterpreted(); 228 return 79.3d; 229 } 230 $noinline$run3A()231 public static float $noinline$run3A() { 232 assertIsManaged(); 233 // Prevent inlining. 234 if (sFlag) { 235 throw new Error(); 236 } 237 // Deoptimize callers. 238 deoptimizeAll(); 239 assertIsInterpreted(); 240 assertCallerIsInterpreted(); // $noinline$run3B is deoptimizeable. 241 return 0.034f; 242 } 243 $noinline$run3B()244 public static float $noinline$run3B() { 245 assertIsManaged(); 246 // Prevent inlining. 247 if (sFlag) { 248 throw new Error(); 249 } 250 float res = $noinline$run3A(); 251 assertIsInterpreted(); 252 return res; 253 } 254 } 255