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