1 /* 2 * @test 3 * @bug 8073480 4 * @summary explicit range checks should be recognized by C2 5 * @library /testlibrary /../../test/lib /compiler/whitebox 6 * @build TestExplicitRangeChecks 7 * @run main ClassFileInstaller sun.hotspot.WhiteBox 8 * @run main ClassFileInstaller com.oracle.java.testlibrary.Platform 9 * @run main/othervm -ea -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 10 * -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:-TieredCompilation -XX:CompileCommand=compileonly,TestExplicitRangeChecks.test* TestExplicitRangeChecks 11 * 12 */ 13 14 import java.lang.annotation.*; 15 import java.lang.reflect.*; 16 import java.util.*; 17 import sun.hotspot.WhiteBox; 18 import sun.hotspot.code.NMethod; 19 import com.oracle.java.testlibrary.Platform; 20 21 public class TestExplicitRangeChecks { 22 23 static int[] array = new int[10]; 24 25 @Retention(RetentionPolicy.RUNTIME) 26 @interface Args { 27 int[] compile(); 28 int[] good(); 29 int[] bad(); 30 boolean deoptimize() default true; 31 } 32 33 // Should be compiled as a single unsigned comparison 34 // 0 <= index < array.length 35 @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10}) 36 static boolean test1_1(int index, int[] array) { 37 if (index < 0 || index >= array.length) { 38 return false; 39 } 40 return true; 41 } 42 43 // same test but so we can compile with same optimization after trap in test1_1 44 static boolean test1_2(int index, int[] array) { 45 if (index < 0 || index >= array.length) { 46 return false; 47 } 48 return true; 49 } 50 51 // Shouldn't matter whether first or second test is the one 52 // against a constants 53 // 0 <= index < array.length 54 @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10}) 55 static boolean test2_1(int index, int[] array) { 56 if (index >= array.length || index < 0) { 57 return false; 58 } 59 return true; 60 } 61 62 static boolean test2_2(int index, int[] array) { 63 if (index >= array.length || index < 0) { 64 return false; 65 } 66 return true; 67 } 68 69 // 0 <= index <= array.length 70 @Args(compile = {5,}, good = {0, 10}, bad = {-1, 11}) 71 static boolean test3_1(int index, int[] array) { 72 if (index < 0 || index > array.length) { 73 return false; 74 } 75 return true; 76 } 77 78 static boolean test3_2(int index, int[] array) { 79 if (index < 0 || index > array.length) { 80 return false; 81 } 82 return true; 83 } 84 85 // 0 <= index <= array.length 86 @Args(compile = {5,}, good = {0, 10}, bad = {-1, 11}) 87 static boolean test4_1(int index, int[] array) { 88 if (index > array.length || index < 0 ) { 89 return false; 90 } 91 return true; 92 } 93 94 static boolean test4_2(int index, int[] array) { 95 if (index > array.length || index < 0) { 96 return false; 97 } 98 return true; 99 } 100 101 static int[] test5_helper(int i) { 102 return (i < 100) ? new int[10] : new int[5]; 103 } 104 105 // 0 < index < array.length 106 @Args(compile = {5,}, good = {1, 9}, bad = {0, 10}) 107 static boolean test5_1(int index, int[] array) { 108 array = test5_helper(index); // array.length must be not constant greater than 1 109 if (index <= 0 || index >= array.length) { 110 return false; 111 } 112 return true; 113 } 114 115 static boolean test5_2(int index, int[] array) { 116 array = test5_helper(index); // array.length must be not constant greater than 1 117 if (index <= 0 || index >= array.length) { 118 return false; 119 } 120 return true; 121 } 122 123 // 0 < index < array.length 124 @Args(compile = {5,}, good = {1, 9}, bad = {0, 10}) 125 static boolean test6_1(int index, int[] array) { 126 array = test5_helper(index); // array.length must be not constant greater than 1 127 if (index >= array.length || index <= 0 ) { 128 return false; 129 } 130 return true; 131 } 132 133 static boolean test6_2(int index, int[] array) { 134 array = test5_helper(index); // array.length must be not constant greater than 1 135 if (index >= array.length || index <= 0) { 136 return false; 137 } 138 return true; 139 } 140 141 // 0 < index <= array.length 142 @Args(compile = {5,}, good = {1, 10}, bad = {0, 11}) 143 static boolean test7_1(int index, int[] array) { 144 if (index <= 0 || index > array.length) { 145 return false; 146 } 147 return true; 148 } 149 150 static boolean test7_2(int index, int[] array) { 151 if (index <= 0 || index > array.length) { 152 return false; 153 } 154 return true; 155 } 156 157 // 0 < index <= array.length 158 @Args(compile = {5,}, good = {1, 10}, bad = {0, 11}) 159 static boolean test8_1(int index, int[] array) { 160 if (index > array.length || index <= 0 ) { 161 return false; 162 } 163 return true; 164 } 165 166 static boolean test8_2(int index, int[] array) { 167 if (index > array.length || index <= 0) { 168 return false; 169 } 170 return true; 171 } 172 173 static int[] test9_helper1(int i) { 174 return (i < 100) ? new int[1] : new int[2]; 175 } 176 177 static int[] test9_helper2(int i) { 178 return (i < 100) ? new int[10] : new int[11]; 179 } 180 181 // array1.length <= index < array2.length 182 @Args(compile = {5,}, good = {1, 9}, bad = {0, 10}) 183 static boolean test9_1(int index, int[] array) { 184 int[] array1 = test9_helper1(index); 185 int[] array2 = test9_helper2(index); 186 if (index < array1.length || index >= array2.length) { 187 return false; 188 } 189 return true; 190 } 191 192 static boolean test9_2(int index, int[] array) { 193 int[] array1 = test9_helper1(index); 194 int[] array2 = test9_helper2(index); 195 if (index < array1.length || index >= array2.length) { 196 return false; 197 } 198 return true; 199 } 200 201 // Previously supported pattern 202 @Args(compile = {-5,5,15}, good = {0, 9}, bad = {-1, 10}, deoptimize=false) 203 static boolean test10_1(int index, int[] array) { 204 if (index < 0 || index >= 10) { 205 return false; 206 } 207 return true; 208 } 209 210 static int[] array11 = new int[10]; 211 @Args(compile = {5,}, good = {0, 9}, bad = {-1,}) 212 static boolean test11_1(int index, int[] array) { 213 if (index < 0) { 214 return false; 215 } 216 int unused = array11[index]; 217 // If this one is folded with the first test then we allow 218 // array access above to proceed even for out of bound array 219 // index and the method throws an 220 // ArrayIndexOutOfBoundsException. 221 if (index >= array.length) { 222 return false; 223 } 224 return true; 225 } 226 227 static int[] array12 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10}; 228 @Args(compile = {5,}, good = {0, 9}, bad = {-1,}) 229 static boolean test12_1(int index, int[] array) { 230 // Cannot be folded otherwise would cause incorrect array 231 // access if the array12 range check is executed before the 232 // folded test. 233 if (index < 0 || index >= array12[index]) { 234 return false; 235 } 236 return true; 237 } 238 239 // Same as test1_1 but pass null array when index < 0: shouldn't 240 // cause NPE. 241 @Args(compile = {5,}, good = {0, 9}, bad = {}) 242 static boolean test13_1(int index, int[] array) { 243 if (index < 0 || index >= array.length) { 244 return false; 245 } 246 return true; 247 } 248 249 // Same as test10 but with uncommon traps 250 @Args(compile = {5}, good = {0, 9}, bad = {-1, 10}) 251 static boolean test14_1(int index, int[] array) { 252 if (index < 0 || index >= 10) { 253 return false; 254 } 255 return true; 256 } 257 258 static boolean test14_2(int index, int[] array) { 259 if (index < 0 || index >= 10) { 260 return false; 261 } 262 return true; 263 } 264 265 // Same as test13_1 but pass null array: null trap should be reported on first if 266 @Args(compile = {5,}, good = {0, 9}, bad = {}) 267 static boolean test15_1(int index, int[] array) { 268 if (index < 0 || index >= array.length) { 269 return false; 270 } 271 return true; 272 } 273 274 // Same as test1 but with no null check between the integer comparisons 275 @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10}) 276 static boolean test16_1(int index, int[] array) { 277 int l = array.length; 278 if (index < 0 || index >= l) { 279 return false; 280 } 281 return true; 282 } 283 284 static boolean test16_2(int index, int[] array) { 285 int l = array.length; 286 if (index < 0 || index >= l) { 287 return false; 288 } 289 return true; 290 } 291 292 // Same as test1 but bound check on array access should optimize 293 // out. 294 @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10}) 295 static boolean test17_1(int index, int[] array) { 296 if (index < 0 || index >= array.length) { 297 return false; 298 } 299 array[index] = 0; 300 return true; 301 } 302 303 static boolean test17_2(int index, int[] array) { 304 if (index < 0 || index >= array.length) { 305 return false; 306 } 307 array[index] = 0; 308 return true; 309 } 310 311 // Same as test1 but range check smearing should optimize 312 // 3rd range check out. 313 @Args(compile = {5,}, good = {}, bad = {}) 314 static boolean test18_1(int index, int[] array) { 315 if (index < 0 || index >= array.length) { 316 return false; 317 } 318 array[index+2] = 0; 319 array[index+1] = 0; 320 return true; 321 } 322 323 static boolean test19_helper1(int index) { 324 if (index < 12) { 325 return false; 326 } 327 return true; 328 } 329 330 static boolean test19_helper2(int index) { 331 if (index > 8) { 332 return false; 333 } 334 return true; 335 } 336 337 // Second test should be optimized out 338 static boolean test19(int index, int[] array) { 339 test19_helper1(index); 340 test19_helper2(index); 341 return true; 342 } 343 344 static boolean success = true; 345 346 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 347 348 final HashMap<String,Method> tests = new HashMap<>(); 349 { 350 final Class<?> TEST_PARAM_TYPES[] = { int[].class, int.class, boolean.class }; 351 for (Method m : this.getClass().getDeclaredMethods()) { 352 if (m.getName().matches("test[0-9]+(_[0-9])?")) { 353 assert(Modifier.isStatic(m.getModifiers())) : m; 354 tests.put(m.getName(), m); 355 } 356 } 357 } 358 359 void doTest(String name) throws Exception { 360 Method m = tests.get(name + "_1"); 361 362 Args anno = m.getAnnotation(Args.class); 363 int[] compile = anno.compile(); 364 int[] good = anno.good(); 365 int[] bad = anno.bad(); 366 boolean deoptimize = anno.deoptimize(); 367 368 // Get compiled 369 for (int i = 0; i < 20000;) { 370 for (int j = 0; j < compile.length; j++) { 371 m.invoke(null, compile[j], array); 372 i++; 373 } 374 } 375 376 if (!WHITE_BOX.isMethodCompiled(m)) { 377 System.out.println(name + "_1 not compiled"); 378 success = false; 379 } 380 381 // check that good values don't trigger exception or 382 // deoptimization 383 for (int i = 0; i < good.length; i++) { 384 boolean res = (boolean)m.invoke(null, good[i], array); 385 386 if (!res) { 387 System.out.println(name + " bad result for good input " + good[i]); 388 success = false; 389 } 390 if (!WHITE_BOX.isMethodCompiled(m)) { 391 System.out.println(name + " deoptimized on valid access"); 392 success = false; 393 } 394 } 395 396 // check that bad values trigger exception and deoptimization 397 for (int i = 0; i < bad.length; i++) { 398 if (i > 0 && deoptimize) { 399 m = tests.get(name + "_" + (i+1)); 400 for (int k = 0; k < 20000;) { 401 for (int j = 0; j < compile.length; j++) { 402 m.invoke(null, compile[j], array); 403 k++; 404 } 405 } 406 if (!WHITE_BOX.isMethodCompiled(m)) { 407 System.out.println(name + ("_" + (i+1)) + " not compiled"); 408 success = false; 409 } 410 } 411 412 boolean res = (boolean)m.invoke(null, bad[i], array); 413 414 if (res) { 415 System.out.println(name + " bad result for bad input " + bad[i]); 416 success = false; 417 } 418 if (Platform.isServer()) { 419 if (deoptimize && WHITE_BOX.isMethodCompiled(m)) { 420 System.out.println(name + " not deoptimized on invalid access"); 421 success = false; 422 } else if (!deoptimize && !WHITE_BOX.isMethodCompiled(m)) { 423 System.out.println(name + " deoptimized on invalid access"); 424 success = false; 425 } 426 } 427 } 428 429 } 430 431 static public void main(String[] args) throws Exception { 432 433 if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { 434 throw new AssertionError("Background compilation enabled"); 435 } 436 437 TestExplicitRangeChecks test = new TestExplicitRangeChecks(); 438 439 test.doTest("test1"); 440 test.doTest("test2"); 441 test.doTest("test3"); 442 test.doTest("test4"); 443 444 // pollute branch profile 445 for (int i = 0; i < 10000; i++) { 446 test5_helper((i%2 == 0) ? 0 : 1000); 447 } 448 449 test.doTest("test5"); 450 test.doTest("test6"); 451 test.doTest("test7"); 452 test.doTest("test8"); 453 454 // pollute branch profile 455 for (int i = 0; i < 10000; i++) { 456 test9_helper1((i%2 == 0) ? 0 : 1000); 457 test9_helper2((i%2 == 0) ? 0 : 1000); 458 } 459 460 test.doTest("test9"); 461 test.doTest("test10"); 462 test.doTest("test11"); 463 test.doTest("test12"); 464 465 test.doTest("test13"); 466 { 467 Method m = test.tests.get("test13_1"); 468 for (int i = 0; i < 1; i++) { 469 test13_1(-1, null); 470 if (!WHITE_BOX.isMethodCompiled(m)) { 471 break; 472 } 473 } 474 } 475 test.doTest("test13"); 476 { 477 Method m = test.tests.get("test13_1"); 478 for (int i = 0; i < 10; i++) { 479 test13_1(-1, null); 480 if (!WHITE_BOX.isMethodCompiled(m)) { 481 break; 482 } 483 } 484 } 485 486 test.doTest("test14"); 487 488 test.doTest("test15"); 489 { 490 Method m = test.tests.get("test15_1"); 491 for (int i = 0; i < 10; i++) { 492 try { 493 test15_1(5, null); 494 } catch(NullPointerException npe) {} 495 if (!WHITE_BOX.isMethodCompiled(m)) { 496 break; 497 } 498 } 499 } 500 test.doTest("test15"); 501 test.doTest("test16"); 502 test.doTest("test17"); 503 test.doTest("test18"); 504 505 for (int i = 0; i < 20000; i++) { 506 test19_helper1(20); 507 test19_helper2(5); 508 } 509 510 { 511 Method m = test.tests.get("test19"); 512 WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); 513 } 514 515 if (!success) { 516 throw new RuntimeException("some tests failed"); 517 } 518 } 519 }