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