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
  30  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  31  *                                jdk.test.lib.Platform
  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 {
  61         int[] compile();
  62         int[] good();
  63         int[] bad();
  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})
  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
  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})
  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 
  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})
 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 
 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})
 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 
 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 
 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})
 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 
 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})
 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 
 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})
 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 
 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})
 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 
 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 
 207     static int[] test9_helper1(int i) {
 208         return (i < 100) ? new int[1] : new int[2];
 209     }
 210 
 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})
 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 
 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)
 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,})
 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,})
 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 = {})
 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})
 285     static boolean test14_1(int index, int[] array) {
 286         if (index < 0 || index >= 10) {
 287             return false;
 288         }
 289         return true;
 290     }
 291 
 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 = {})
 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})
 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 
 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})
 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 
 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 = {})
 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 
 357     static boolean test19_helper1(int index) {
 358         if (index < 12) {
 359             return false;
 360         }
 361         return true;
 362     }
 363 
 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
 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;
 383                 tests.put(m.getName(), m);
 384             }
 385         }
 386     }
 387 
 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() &&
 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
 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 
 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 
 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 
 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 
 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 }