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