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