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 /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 import compiler.whitebox.CompilerWhiteBoxTest; 45 46 public class TestExplicitRangeChecks { 47 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 48 private static final int TIERED_STOP_AT_LEVEL = WHITE_BOX.getIntxVMFlag("TieredStopAtLevel").intValue(); 49 private static int[] array = new int[10]; 50 private static boolean success = true; 51 52 @Retention(RetentionPolicy.RUNTIME) 53 @interface Args { 54 int[] compile(); 55 int[] good(); 56 int[] bad(); 57 boolean deoptimize() default true; 58 } 59 60 // Should be compiled as a single unsigned comparison 61 // 0 <= index < array.length 62 @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10}) 63 static boolean test1_1(int index, int[] array) { 64 if (index < 0 || index >= array.length) { 65 return false; 66 } 67 return true; 68 } 69 70 // same test but so we can compile with same optimization after trap in test1_1 71 static boolean test1_2(int index, int[] array) { 72 if (index < 0 || index >= array.length) { 73 return false; 74 } 75 return true; 76 } 77 78 // Shouldn't matter whether first or second test is the one 79 // against a constants 80 // 0 <= index < array.length 81 @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10}) 82 static boolean test2_1(int index, int[] array) { 83 if (index >= array.length || index < 0) { 84 return false; 85 } 86 return true; 87 } 88 89 static boolean test2_2(int index, int[] array) { 90 if (index >= array.length || index < 0) { 91 return false; 92 } 93 return true; 94 } 95 96 // 0 <= index <= array.length 97 @Args(compile = {5,}, good = {0, 10}, bad = {-1, 11}) 98 static boolean test3_1(int index, int[] array) { 99 if (index < 0 || index > array.length) { 100 return false; 101 } 102 return true; 103 } 104 105 static boolean test3_2(int index, int[] array) { 106 if (index < 0 || index > array.length) { 107 return false; 108 } 109 return true; 110 } 111 112 // 0 <= index <= array.length 113 @Args(compile = {5,}, good = {0, 10}, bad = {-1, 11}) 114 static boolean test4_1(int index, int[] array) { 115 if (index > array.length || index < 0 ) { 116 return false; 117 } 118 return true; 119 } 120 121 static boolean test4_2(int index, int[] array) { 122 if (index > array.length || index < 0) { 123 return false; 124 } 125 return true; 126 } 127 128 static int[] test5_helper(int i) { 129 return (i < 100) ? new int[10] : new int[5]; 130 } 131 132 // 0 < index < array.length 133 @Args(compile = {5,}, good = {1, 9}, bad = {0, 10}) 134 static boolean test5_1(int index, int[] array) { 135 array = test5_helper(index); // array.length must be not constant greater than 1 136 if (index <= 0 || index >= array.length) { 137 return false; 138 } 139 return true; 140 } 141 142 static boolean test5_2(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 // 0 < index < array.length 151 @Args(compile = {5,}, good = {1, 9}, bad = {0, 10}) 152 static boolean test6_1(int index, int[] array) { 153 array = test5_helper(index); // array.length must be not constant greater than 1 154 if (index >= array.length || index <= 0 ) { 155 return false; 156 } 157 return true; 158 } 159 160 static boolean test6_2(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 // 0 < index <= array.length 169 @Args(compile = {5,}, good = {1, 10}, bad = {0, 11}) 170 static boolean test7_1(int index, int[] array) { 171 if (index <= 0 || index > array.length) { 172 return false; 173 } 174 return true; 175 } 176 177 static boolean test7_2(int index, int[] array) { 178 if (index <= 0 || index > array.length) { 179 return false; 180 } 181 return true; 182 } 183 184 // 0 < index <= array.length 185 @Args(compile = {5,}, good = {1, 10}, bad = {0, 11}) 186 static boolean test8_1(int index, int[] array) { 187 if (index > array.length || index <= 0 ) { 188 return false; 189 } 190 return true; 191 } 192 193 static boolean test8_2(int index, int[] array) { 194 if (index > array.length || index <= 0) { 195 return false; 196 } 197 return true; 198 } 199 200 static int[] test9_helper1(int i) { 201 return (i < 100) ? new int[1] : new int[2]; 202 } 203 204 static int[] test9_helper2(int i) { 205 return (i < 100) ? new int[10] : new int[11]; 206 } 207 208 // array1.length <= index < array2.length 209 @Args(compile = {5,}, good = {1, 9}, bad = {0, 10}) 210 static boolean test9_1(int index, int[] array) { 211 int[] array1 = test9_helper1(index); 212 int[] array2 = test9_helper2(index); 213 if (index < array1.length || index >= array2.length) { 214 return false; 215 } 216 return true; 217 } 218 219 static boolean test9_2(int index, int[] array) { 220 int[] array1 = test9_helper1(index); 221 int[] array2 = test9_helper2(index); 222 if (index < array1.length || index >= array2.length) { 223 return false; 224 } 225 return true; 226 } 227 228 // Previously supported pattern 229 @Args(compile = {-5,5,15}, good = {0, 9}, bad = {-1, 10}, deoptimize=false) 230 static boolean test10_1(int index, int[] array) { 231 if (index < 0 || index >= 10) { 232 return false; 233 } 234 return true; 235 } 236 237 static int[] array11 = new int[10]; 238 @Args(compile = {5,}, good = {0, 9}, bad = {-1,}) 239 static boolean test11_1(int index, int[] array) { 240 if (index < 0) { 241 return false; 242 } 243 int unused = array11[index]; 244 // If this one is folded with the first test then we allow 245 // array access above to proceed even for out of bound array 246 // index and the method throws an 247 // ArrayIndexOutOfBoundsException. 248 if (index >= array.length) { 249 return false; 250 } 251 return true; 252 } 253 254 static int[] array12 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10}; 255 @Args(compile = {5,}, good = {0, 9}, bad = {-1,}) 256 static boolean test12_1(int index, int[] array) { 257 // Cannot be folded otherwise would cause incorrect array 258 // access if the array12 range check is executed before the 259 // folded test. 260 if (index < 0 || index >= array12[index]) { 261 return false; 262 } 263 return true; 264 } 265 266 // Same as test1_1 but pass null array when index < 0: shouldn't 267 // cause NPE. 268 @Args(compile = {5,}, good = {0, 9}, bad = {}) 269 static boolean test13_1(int index, int[] array) { 270 if (index < 0 || index >= array.length) { 271 return false; 272 } 273 return true; 274 } 275 276 // Same as test10 but with uncommon traps 277 @Args(compile = {5}, good = {0, 9}, bad = {-1, 10}) 278 static boolean test14_1(int index, int[] array) { 279 if (index < 0 || index >= 10) { 280 return false; 281 } 282 return true; 283 } 284 285 static boolean test14_2(int index, int[] array) { 286 if (index < 0 || index >= 10) { 287 return false; 288 } 289 return true; 290 } 291 292 // Same as test13_1 but pass null array: null trap should be reported on first if 293 @Args(compile = {5,}, good = {0, 9}, bad = {}) 294 static boolean test15_1(int index, int[] array) { 295 if (index < 0 || index >= array.length) { 296 return false; 297 } 298 return true; 299 } 300 301 // Same as test1 but with no null check between the integer comparisons 302 @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10}) 303 static boolean test16_1(int index, int[] array) { 304 int l = array.length; 305 if (index < 0 || index >= l) { 306 return false; 307 } 308 return true; 309 } 310 311 static boolean test16_2(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 // Same as test1 but bound check on array access should optimize 320 // out. 321 @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10}) 322 static boolean test17_1(int index, int[] array) { 323 if (index < 0 || index >= array.length) { 324 return false; 325 } 326 array[index] = 0; 327 return true; 328 } 329 330 static boolean test17_2(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 // Same as test1 but range check smearing should optimize 339 // 3rd range check out. 340 @Args(compile = {5,}, good = {}, bad = {}) 341 static boolean test18_1(int index, int[] array) { 342 if (index < 0 || index >= array.length) { 343 return false; 344 } 345 array[index+2] = 0; 346 array[index+1] = 0; 347 return true; 348 } 349 350 static boolean test19_helper1(int index) { 351 if (index < 12) { 352 return false; 353 } 354 return true; 355 } 356 357 static boolean test19_helper2(int index) { 358 if (index > 8) { 359 return false; 360 } 361 return true; 362 } 363 364 // Second test should be optimized out 365 static boolean test19(int index, int[] array) { 366 test19_helper1(index); 367 test19_helper2(index); 368 return true; 369 } 370 371 final HashMap<String,Method> tests = new HashMap<>(); 372 { 373 for (Method m : this.getClass().getDeclaredMethods()) { 374 if (m.getName().matches("test[0-9]+(_[0-9])?")) { 375 assert(Modifier.isStatic(m.getModifiers())) : m; 376 tests.put(m.getName(), m); 377 } 378 } 379 } 380 381 void doTest(String name) throws Exception { 382 Method m = tests.get(name + "_1"); 383 384 Args anno = m.getAnnotation(Args.class); 385 int[] compile = anno.compile(); 386 int[] good = anno.good(); 387 int[] bad = anno.bad(); 388 boolean deoptimize = anno.deoptimize(); 389 390 // Get compiled 391 for (int i = 0; i < 20000;) { 392 for (int j = 0; j < compile.length; j++) { 393 m.invoke(null, compile[j], array); 394 i++; 395 } 396 } 397 398 if (!WHITE_BOX.isMethodCompiled(m)) { 399 System.out.println(name + "_1 not compiled"); 400 success = false; 401 } 402 403 // check that good values don't trigger exception or 404 // deoptimization 405 for (int i = 0; i < good.length; i++) { 406 boolean res = (boolean)m.invoke(null, good[i], array); 407 408 if (!res) { 409 System.out.println(name + " bad result for good input " + good[i]); 410 success = false; 411 } 412 if (!WHITE_BOX.isMethodCompiled(m)) { 413 System.out.println(name + " deoptimized on valid access"); 414 success = false; 415 } 416 } 417 418 // check that bad values trigger exception and deoptimization 419 for (int i = 0; i < bad.length; i++) { 420 if (i > 0 && deoptimize) { 421 m = tests.get(name + "_" + (i+1)); 422 for (int k = 0; k < 20000;) { 423 for (int j = 0; j < compile.length; j++) { 424 m.invoke(null, compile[j], array); 425 k++; 426 } 427 } 428 if (!WHITE_BOX.isMethodCompiled(m)) { 429 System.out.println(name + ("_" + (i+1)) + " not compiled"); 430 success = false; 431 } 432 } 433 434 boolean res = (boolean)m.invoke(null, bad[i], array); 435 436 if (res) { 437 System.out.println(name + " bad result for bad input " + bad[i]); 438 success = false; 439 } 440 // Only perform these additional checks if C2 is available 441 if (Platform.isServer() && 442 TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) { 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 }