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