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