1 /* 2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8066103 27 * @summary C2's range check smearing allows out of bound array accesses 28 * @library /testlibrary /test/lib / 29 * @modules java.base/jdk.internal.misc 30 * java.management 31 * @build compiler.rangechecks.TestRangeCheckSmearing 32 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 33 * jdk.test.lib.Platform 34 * @run main/othervm -ea -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 35 * -XX:-BackgroundCompilation -XX:-UseOnStackReplacement 36 * compiler.rangechecks.TestRangeCheckSmearing 37 * 38 */ 39 40 package compiler.rangechecks; 41 42 import compiler.whitebox.CompilerWhiteBoxTest; 43 import jdk.test.lib.Platform; 44 import sun.hotspot.WhiteBox; 45 46 import java.lang.annotation.Retention; 47 import java.lang.annotation.RetentionPolicy; 48 import java.lang.reflect.Method; 49 import java.lang.reflect.Modifier; 50 import java.util.Arrays; 51 import java.util.HashMap; 52 53 public class TestRangeCheckSmearing { 54 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 55 56 @Retention(RetentionPolicy.RUNTIME) 57 @interface Args { int[] value(); } 58 59 // first range check is i + max of all constants 60 @Args({0, 8}) 61 static int m1(int[] array, int i, boolean allaccesses) { 62 int res = 0; 63 res += array[i+9]; 64 if (allaccesses) { 65 res += array[i+8]; 66 res += array[i+7]; 67 res += array[i+6]; 68 res += array[i+5]; 69 res += array[i+4]; 70 res += array[i+3]; 71 res += array[i+2]; 72 res += array[i+1]; 73 } 74 return res; 75 } 76 77 // first range check is i + min of all constants 78 @Args({0, -9}) 79 static int m2(int[] array, int i, boolean allaccesses) { 80 int res = 0; 81 res += array[i+1]; 82 if (allaccesses) { 83 res += array[i+2]; 84 res += array[i+3]; 85 res += array[i+4]; 86 res += array[i+5]; 87 res += array[i+6]; 88 res += array[i+7]; 89 res += array[i+8]; 90 res += array[i+9]; 91 } 92 return res; 93 } 94 95 // first range check is not i + min/max of all constants 96 @Args({0, 8}) 97 static int m3(int[] array, int i, boolean allaccesses) { 98 int res = 0; 99 res += array[i+3]; 100 if (allaccesses) { 101 res += array[i+2]; 102 res += array[i+1]; 103 res += array[i+4]; 104 res += array[i+5]; 105 res += array[i+6]; 106 res += array[i+7]; 107 res += array[i+8]; 108 res += array[i+9]; 109 } 110 return res; 111 } 112 113 @Args({0, -9}) 114 static int m4(int[] array, int i, boolean allaccesses) { 115 int res = 0; 116 res += array[i+3]; 117 if (allaccesses) { 118 res += array[i+4]; 119 res += array[i+1]; 120 res += array[i+2]; 121 res += array[i+5]; 122 res += array[i+6]; 123 res += array[i+7]; 124 res += array[i+8]; 125 res += array[i+9]; 126 } 127 return res; 128 } 129 130 @Args({0, -3}) 131 static int m5(int[] array, int i, boolean allaccesses) { 132 int res = 0; 133 res += array[i+3]; 134 res += array[i+2]; 135 if (allaccesses) { 136 res += array[i+1]; 137 res += array[i+4]; 138 res += array[i+5]; 139 res += array[i+6]; 140 res += array[i+7]; 141 res += array[i+8]; 142 res += array[i+9]; 143 } 144 return res; 145 } 146 147 @Args({0, 6}) 148 static int m6(int[] array, int i, boolean allaccesses) { 149 int res = 0; 150 res += array[i+3]; 151 res += array[i+4]; 152 if (allaccesses) { 153 res += array[i+2]; 154 res += array[i+1]; 155 res += array[i+5]; 156 res += array[i+6]; 157 res += array[i+7]; 158 res += array[i+8]; 159 res += array[i+9]; 160 } 161 return res; 162 } 163 164 @Args({0, 6}) 165 static int m7(int[] array, int i, boolean allaccesses) { 166 int res = 0; 167 res += array[i+3]; 168 res += array[i+2]; 169 res += array[i+4]; 170 if (allaccesses) { 171 res += array[i+1]; 172 res += array[i+5]; 173 res += array[i+6]; 174 res += array[i+7]; 175 res += array[i+8]; 176 res += array[i+9]; 177 } 178 return res; 179 } 180 181 @Args({0, -3}) 182 static int m8(int[] array, int i, boolean allaccesses) { 183 int res = 0; 184 res += array[i+3]; 185 res += array[i+4]; 186 res += array[i+2]; 187 if (allaccesses) { 188 res += array[i+1]; 189 res += array[i+5]; 190 res += array[i+6]; 191 res += array[i+7]; 192 res += array[i+8]; 193 res += array[i+9]; 194 } 195 return res; 196 } 197 198 @Args({6, 15}) 199 static int m9(int[] array, int i, boolean allaccesses) { 200 int res = 0; 201 res += array[i+3]; 202 if (allaccesses) { 203 res += array[i-2]; 204 res += array[i-1]; 205 res += array[i-4]; 206 res += array[i-5]; 207 res += array[i-6]; 208 } 209 return res; 210 } 211 212 @Args({3, 12}) 213 static int m10(int[] array, int i, boolean allaccesses) { 214 int res = 0; 215 res += array[i+3]; 216 if (allaccesses) { 217 res += array[i-2]; 218 res += array[i-1]; 219 res += array[i-3]; 220 res += array[i+4]; 221 res += array[i+5]; 222 res += array[i+6]; 223 } 224 return res; 225 } 226 227 @Args({3, -3}) 228 static int m11(int[] array, int i, boolean allaccesses) { 229 int res = 0; 230 res += array[i+3]; 231 res += array[i-2]; 232 if (allaccesses) { 233 res += array[i+5]; 234 res += array[i+6]; 235 } 236 return res; 237 } 238 239 @Args({3, 6}) 240 static int m12(int[] array, int i, boolean allaccesses) { 241 int res = 0; 242 res += array[i+3]; 243 res += array[i+6]; 244 if (allaccesses) { 245 res += array[i-2]; 246 res += array[i-3]; 247 } 248 return res; 249 } 250 251 // check that identical range check is replaced by dominating one 252 // only when correct 253 @Args({0}) 254 static int m13(int[] array, int i, boolean ignore) { 255 int res = 0; 256 res += array[i+3]; 257 res += array[i+3]; 258 return res; 259 } 260 261 @Args({2, 0}) 262 static int m14(int[] array, int i, boolean ignore) { 263 int res = 0; 264 265 res += array[i]; 266 res += array[i-2]; 267 res += array[i]; // If range check below were to be removed first this cannot be considered identical to first range check 268 res += array[i-1]; // range check removed so i-1 array access depends on previous check 269 270 return res; 271 } 272 273 static int[] m15_dummy = new int[10]; 274 @Args({2, 0}) 275 static int m15(int[] array, int i, boolean ignore) { 276 int res = 0; 277 res += array[i]; 278 279 // When the loop is optimized out we don't want the 280 // array[i-1] access which is dependent on array[i]'s 281 // range check to become dependent on the identical range 282 // check above. 283 284 int[] array2 = m15_dummy; 285 int j = 0; 286 for (; j < 10; j++); 287 if (j == 10) { 288 array2 = array; 289 } 290 291 res += array2[i-2]; 292 res += array2[i]; 293 res += array2[i-1]; // range check removed so i-1 array access depends on previous check 294 295 return res; 296 } 297 298 @Args({2, 0}) 299 static int m16(int[] array, int i, boolean ignore) { 300 int res = 0; 301 302 res += array[i]; 303 res += array[i-1]; 304 res += array[i-1]; 305 res += array[i-2]; 306 307 return res; 308 } 309 310 @Args({2, 0}) 311 static int m17(int[] array, int i, boolean ignore) { 312 int res = 0; 313 314 res += array[i]; 315 res += array[i-2]; 316 res += array[i-2]; 317 res += array[i+2]; 318 res += array[i+2]; 319 res += array[i-1]; 320 res += array[i-1]; 321 322 return res; 323 } 324 325 static public void main(String[] args) { 326 if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { 327 throw new AssertionError("Background compilation enabled"); 328 } 329 new TestRangeCheckSmearing().doTests(); 330 } 331 boolean success = true; 332 boolean exception = false; 333 final int[] array = new int[10]; 334 final HashMap<String,Method> tests = new HashMap<>(); 335 { 336 final Class<?> TEST_PARAM_TYPES[] = { int[].class, int.class, boolean.class }; 337 for (Method m : this.getClass().getDeclaredMethods()) { 338 if (m.getName().matches("m[0-9]+")) { 339 assert(Modifier.isStatic(m.getModifiers())) : m; 340 assert(m.getReturnType() == int.class) : m; 341 assert(Arrays.equals(m.getParameterTypes(), TEST_PARAM_TYPES)) : m; 342 tests.put(m.getName(), m); 343 } 344 } 345 } 346 347 void invokeTest(Method m, int[] array, int index, boolean z) { 348 try { 349 m.invoke(null, array, index, z); 350 } catch (ReflectiveOperationException roe) { 351 Throwable ex = roe.getCause(); 352 if (ex instanceof ArrayIndexOutOfBoundsException) 353 throw (ArrayIndexOutOfBoundsException) ex; 354 throw new AssertionError(roe); 355 } 356 } 357 358 void doTest(String name) { 359 Method m = tests.get(name); 360 tests.remove(name); 361 int[] args = m.getAnnotation(Args.class).value(); 362 int index0 = args[0], index1; 363 boolean exceptionRequired = true; 364 if (args.length == 2) { 365 index1 = args[1]; 366 } else { 367 // no negative test for this one 368 assert(args.length == 1); 369 assert(name.equals("m13")); 370 exceptionRequired = false; 371 index1 = index0; 372 } 373 // Get the method compiled. 374 if (!WHITE_BOX.isMethodCompiled(m)) { 375 // If not, try to compile it with C2 376 if(!WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) { 377 // C2 compiler not available, try to compile with C1 378 WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE); 379 } 380 } 381 if (!WHITE_BOX.isMethodCompiled(m)) { 382 throw new RuntimeException(m + " not compiled"); 383 } 384 385 // valid access 386 invokeTest(m, array, index0, true); 387 388 if (!WHITE_BOX.isMethodCompiled(m)) { 389 throw new RuntimeException(m + " deoptimized on valid array access"); 390 } 391 392 exception = false; 393 boolean test_success = true; 394 try { 395 invokeTest(m, array, index1, false); 396 } catch(ArrayIndexOutOfBoundsException aioob) { 397 exception = true; 398 System.out.println("ArrayIndexOutOfBoundsException thrown in "+name); 399 } 400 if (!exception) { 401 System.out.println("ArrayIndexOutOfBoundsException was not thrown in "+name); 402 } 403 404 if (Platform.isServer()) { 405 if (exceptionRequired == WHITE_BOX.isMethodCompiled(m)) { 406 System.out.println((exceptionRequired?"Didn't deoptimized":"deoptimized") + " in "+name); 407 test_success = false; 408 } 409 } 410 411 if (exception != exceptionRequired) { 412 System.out.println((exceptionRequired?"exception required but not thrown":"not exception required but thrown") + " in "+name); 413 test_success = false; 414 } 415 416 if (!test_success) { 417 success = false; 418 System.out.println("TEST FAILED: "+name); 419 } 420 421 } 422 void doTests() { 423 doTest("m1"); 424 doTest("m2"); 425 doTest("m3"); 426 doTest("m4"); 427 doTest("m5"); 428 doTest("m6"); 429 doTest("m7"); 430 doTest("m8"); 431 doTest("m9"); 432 doTest("m10"); 433 doTest("m11"); 434 doTest("m12"); 435 doTest("m13"); 436 doTest("m14"); 437 doTest("m15"); 438 doTest("m16"); 439 doTest("m17"); 440 if (!success) { 441 throw new RuntimeException("Some tests failed"); 442 } 443 assert(tests.isEmpty()) : tests; 444 } 445 }