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 /
  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 
  48     static int[] array = new int[10];
  49 
  50     @Retention(RetentionPolicy.RUNTIME)
  51     @interface Args {
  52         int[] compile();
  53         int[] good();
  54         int[] bad();
  55         boolean deoptimize() default true;
  56     }
  57 
  58     // Should be compiled as a single unsigned comparison
  59     // 0 <= index < array.length
  60     @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})
  61     static boolean test1_1(int index, int[] array) {
  62         if (index < 0 || index >= array.length) {
  63             return false;
  64         }
  65         return true;
  66     }
  67 
  68     // same test but so we can compile with same optimization after trap in test1_1
  69     static boolean test1_2(int index, int[] array) {
  70         if (index < 0 || index >= array.length) {
  71             return false;
  72         }
  73         return true;
  74     }
  75 
  76     // Shouldn't matter whether first or second test is the one
  77     // against a constants
  78     // 0 <= index < array.length
  79     @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})
  80     static boolean test2_1(int index, int[] array) {
  81         if (index >= array.length || index < 0) {
  82             return false;
  83         }
  84         return true;
  85     }
  86 
  87     static boolean test2_2(int index, int[] array) {
  88         if (index >= array.length || index < 0) {
  89             return false;
  90         }
  91         return true;
  92     }
  93 
  94     // 0 <= index <= array.length
  95     @Args(compile = {5,}, good = {0, 10}, bad = {-1, 11})
  96     static boolean test3_1(int index, int[] array) {
  97         if (index < 0 || index > array.length) {
  98             return false;
  99         }
 100         return true;
 101     }
 102 
 103     static boolean test3_2(int index, int[] array) {
 104         if (index < 0 || index > array.length) {
 105             return false;
 106         }
 107         return true;
 108     }
 109 
 110     // 0 <= index <= array.length
 111     @Args(compile = {5,}, good = {0, 10}, bad = {-1, 11})
 112     static boolean test4_1(int index, int[] array) {
 113         if (index > array.length || index < 0 ) {
 114             return false;
 115         }
 116         return true;
 117     }
 118 
 119     static boolean test4_2(int index, int[] array) {
 120         if (index > array.length || index < 0) {
 121             return false;
 122         }
 123         return true;
 124     }
 125 
 126     static int[] test5_helper(int i) {
 127         return (i < 100) ? new int[10] : new int[5];
 128     }
 129 
 130     // 0 < index < array.length
 131     @Args(compile = {5,}, good = {1, 9}, bad = {0, 10})
 132     static boolean test5_1(int index, int[] array) {
 133         array = test5_helper(index); // array.length must be not constant greater than 1
 134         if (index <= 0 || index >= array.length) {
 135             return false;
 136         }
 137         return true;
 138     }
 139 
 140     static boolean test5_2(int index, int[] array) {
 141         array = test5_helper(index); // array.length must be not constant greater than 1
 142         if (index <= 0 || index >= array.length) {
 143             return false;
 144         }
 145         return true;
 146     }
 147 
 148     // 0 < index < array.length
 149     @Args(compile = {5,}, good = {1, 9}, bad = {0, 10})
 150     static boolean test6_1(int index, int[] array) {
 151         array = test5_helper(index); // array.length must be not constant greater than 1
 152         if (index >= array.length || index <= 0 ) {
 153             return false;
 154         }
 155         return true;
 156     }
 157 
 158     static boolean test6_2(int index, int[] array) {
 159         array = test5_helper(index); // array.length must be not constant greater than 1
 160         if (index >= array.length || index <= 0) {
 161             return false;
 162         }
 163         return true;
 164     }
 165 
 166     // 0 < index <= array.length
 167     @Args(compile = {5,}, good = {1, 10}, bad = {0, 11})
 168     static boolean test7_1(int index, int[] array) {
 169         if (index <= 0 || index > array.length) {
 170             return false;
 171         }
 172         return true;
 173     }
 174 
 175     static boolean test7_2(int index, int[] array) {
 176         if (index <= 0 || index > array.length) {
 177             return false;
 178         }
 179         return true;
 180     }
 181 
 182     // 0 < index <= array.length
 183     @Args(compile = {5,}, good = {1, 10}, bad = {0, 11})
 184     static boolean test8_1(int index, int[] array) {
 185         if (index > array.length || index <= 0 ) {
 186             return false;
 187         }
 188         return true;
 189     }
 190 
 191     static boolean test8_2(int index, int[] array) {
 192         if (index > array.length || index <= 0) {
 193             return false;
 194         }
 195         return true;
 196     }
 197 
 198     static int[] test9_helper1(int i) {
 199         return (i < 100) ? new int[1] : new int[2];
 200     }
 201 
 202     static int[] test9_helper2(int i) {
 203         return (i < 100) ? new int[10] : new int[11];
 204     }
 205 
 206     // array1.length <= index < array2.length
 207     @Args(compile = {5,}, good = {1, 9}, bad = {0, 10})
 208     static boolean test9_1(int index, int[] array) {
 209         int[] array1 = test9_helper1(index);
 210         int[] array2 = test9_helper2(index);
 211         if (index < array1.length || index >= array2.length) {
 212             return false;
 213         }
 214         return true;
 215     }
 216 
 217     static boolean test9_2(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     // Previously supported pattern
 227     @Args(compile = {-5,5,15}, good = {0, 9}, bad = {-1, 10}, deoptimize=false)
 228     static boolean test10_1(int index, int[] array) {
 229         if (index < 0 || index >= 10) {
 230             return false;
 231         }
 232         return true;
 233     }
 234 
 235     static int[] array11 = new int[10];
 236     @Args(compile = {5,}, good = {0, 9}, bad = {-1,})
 237     static boolean test11_1(int index, int[] array) {
 238         if (index < 0) {
 239             return false;
 240         }
 241         int unused = array11[index];
 242         // If this one is folded with the first test then we allow
 243         // array access above to proceed even for out of bound array
 244         // index and the method throws an
 245         // ArrayIndexOutOfBoundsException.
 246         if (index >= array.length) {
 247             return false;
 248         }
 249         return true;
 250     }
 251 
 252     static int[] array12 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
 253     @Args(compile = {5,}, good = {0, 9}, bad = {-1,})
 254     static boolean test12_1(int index, int[] array) {
 255         // Cannot be folded otherwise would cause incorrect array
 256         // access if the array12 range check is executed before the
 257         // folded test.
 258         if (index < 0 || index >= array12[index]) {
 259             return false;
 260         }
 261         return true;
 262     }
 263 
 264     // Same as test1_1 but pass null array when index < 0: shouldn't
 265     // cause NPE.
 266     @Args(compile = {5,}, good = {0, 9}, bad = {})
 267     static boolean test13_1(int index, int[] array) {
 268         if (index < 0 || index >= array.length) {
 269             return false;
 270         }
 271         return true;
 272     }
 273 
 274     // Same as test10 but with uncommon traps
 275     @Args(compile = {5}, good = {0, 9}, bad = {-1, 10})
 276     static boolean test14_1(int index, int[] array) {
 277         if (index < 0 || index >= 10) {
 278             return false;
 279         }
 280         return true;
 281     }
 282 
 283     static boolean test14_2(int index, int[] array) {
 284         if (index < 0 || index >= 10) {
 285             return false;
 286         }
 287         return true;
 288     }
 289 
 290     // Same as test13_1 but pass null array: null trap should be reported on first if
 291     @Args(compile = {5,}, good = {0, 9}, bad = {})
 292     static boolean test15_1(int index, int[] array) {
 293         if (index < 0 || index >= array.length) {
 294             return false;
 295         }
 296         return true;
 297     }
 298 
 299     // Same as test1 but with no null check between the integer comparisons
 300     @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})
 301     static boolean test16_1(int index, int[] array) {
 302         int l = array.length;
 303         if (index < 0 || index >= l) {
 304             return false;
 305         }
 306         return true;
 307     }
 308 
 309     static boolean test16_2(int index, int[] array) {
 310         int l = array.length;
 311         if (index < 0 || index >= l) {
 312             return false;
 313         }
 314         return true;
 315     }
 316 
 317     // Same as test1 but bound check on array access should optimize
 318     // out.
 319     @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})
 320     static boolean test17_1(int index, int[] array) {
 321         if (index < 0 || index >= array.length) {
 322             return false;
 323         }
 324         array[index] = 0;
 325         return true;
 326     }
 327 
 328     static boolean test17_2(int index, int[] array) {
 329         if (index < 0 || index >= array.length) {
 330             return false;
 331         }
 332         array[index] = 0;
 333         return true;
 334     }
 335 
 336     // Same as test1 but range check smearing should optimize
 337     // 3rd range check out.
 338     @Args(compile = {5,}, good = {}, bad = {})
 339     static boolean test18_1(int index, int[] array) {
 340         if (index < 0 || index >= array.length) {
 341             return false;
 342         }
 343         array[index+2] = 0;
 344         array[index+1] = 0;
 345         return true;
 346     }
 347 
 348     static boolean test19_helper1(int index) {
 349         if (index < 12) {
 350             return false;
 351         }
 352         return true;
 353     }
 354 
 355     static boolean test19_helper2(int index) {
 356         if (index > 8) {
 357             return false;
 358         }
 359         return true;
 360     }
 361 
 362     // Second test should be optimized out
 363     static boolean test19(int index, int[] array) {
 364         test19_helper1(index);
 365         test19_helper2(index);
 366         return true;
 367     }
 368 
 369     static boolean success = true;
 370 
 371     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
 372 
 373     final HashMap<String,Method> tests = new HashMap<>();
 374     {
 375         for (Method m : this.getClass().getDeclaredMethods()) {
 376             if (m.getName().matches("test[0-9]+(_[0-9])?")) {
 377                 assert(Modifier.isStatic(m.getModifiers())) : m;
 378                 tests.put(m.getName(), m);
 379             }
 380         }
 381     }
 382 
 383     void doTest(String name) throws Exception {
 384         Method m = tests.get(name + "_1");
 385 
 386         Args anno =  m.getAnnotation(Args.class);
 387         int[] compile = anno.compile();
 388         int[] good = anno.good();
 389         int[] bad = anno.bad();
 390         boolean deoptimize = anno.deoptimize();
 391 
 392         // Get compiled
 393         for (int i = 0; i < 20000;) {
 394             for (int j = 0; j < compile.length; j++) {
 395                 m.invoke(null, compile[j], array);
 396                 i++;
 397             }
 398         }
 399 
 400         if (!WHITE_BOX.isMethodCompiled(m)) {
 401             System.out.println(name + "_1 not compiled");
 402             success = false;
 403         }
 404 
 405         // check that good values don't trigger exception or
 406         // deoptimization
 407         for (int i = 0; i < good.length; i++) {
 408             boolean res = (boolean)m.invoke(null, good[i], array);
 409 
 410             if (!res) {
 411                 System.out.println(name + " bad result for good input " + good[i]);
 412                 success = false;
 413             }
 414             if (!WHITE_BOX.isMethodCompiled(m)) {
 415                 System.out.println(name + " deoptimized on valid access");
 416                 success = false;
 417             }
 418         }
 419 
 420         // check that bad values trigger exception and deoptimization
 421         for (int i = 0; i < bad.length; i++) {
 422             if (i > 0 && deoptimize) {
 423                 m = tests.get(name + "_" + (i+1));
 424                 for (int k = 0; k < 20000;) {
 425                     for (int j = 0; j < compile.length; j++) {
 426                         m.invoke(null, compile[j], array);
 427                         k++;
 428                     }
 429                 }
 430                 if (!WHITE_BOX.isMethodCompiled(m)) {
 431                     System.out.println(name + ("_" + (i+1)) + " not compiled");
 432                     success = false;
 433                 }
 434             }
 435 
 436             boolean res = (boolean)m.invoke(null, bad[i], array);
 437 
 438             if (res) {
 439                 System.out.println(name + " bad result for bad input " + bad[i]);
 440                 success = false;
 441             }
 442             if (Platform.isServer()) {
 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 }