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