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