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