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