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 }