1 /*
   2  * Copyright (c) 2014, 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 8066103
  27  * @summary C2's range check smearing allows out of bound array accesses
  28  * @library /testlibrary /test/lib /
  29  * @modules java.base/jdk.internal.misc
  30  *          java.management
  31  * @build TestRangeCheckSmearing
  32  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  33  *                                jdk.test.lib.Platform
  34  * @run main/othervm -ea -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  35  *                   -XX:-BackgroundCompilation -XX:-UseOnStackReplacement TestRangeCheckSmearing
  36  *
  37  */
  38 
  39 import java.lang.annotation.*;
  40 import java.lang.reflect.*;
  41 import java.util.*;
  42 import sun.hotspot.WhiteBox;
  43 import sun.hotspot.code.NMethod;
  44 import jdk.test.lib.Platform;
  45 import compiler.whitebox.CompilerWhiteBoxTest;
  46 
  47 public class TestRangeCheckSmearing {
  48     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
  49 
  50     @Retention(RetentionPolicy.RUNTIME)
  51     @interface Args { int[] value(); }
  52 
  53     // first range check is i + max of all constants
  54     @Args({0, 8})
  55     static int m1(int[] array, int i, boolean allaccesses) {
  56         int res = 0;
  57         res += array[i+9];
  58         if (allaccesses) {
  59             res += array[i+8];
  60             res += array[i+7];
  61             res += array[i+6];
  62             res += array[i+5];
  63             res += array[i+4];
  64             res += array[i+3];
  65             res += array[i+2];
  66             res += array[i+1];
  67         }
  68         return res;
  69     }
  70 
  71     // first range check is i + min of all constants
  72     @Args({0, -9})
  73     static int m2(int[] array, int i, boolean allaccesses) {
  74         int res = 0;
  75         res += array[i+1];
  76         if (allaccesses) {
  77             res += array[i+2];
  78             res += array[i+3];
  79             res += array[i+4];
  80             res += array[i+5];
  81             res += array[i+6];
  82             res += array[i+7];
  83             res += array[i+8];
  84             res += array[i+9];
  85         }
  86         return res;
  87     }
  88 
  89     // first range check is not i + min/max of all constants
  90     @Args({0, 8})
  91     static int m3(int[] array, int i, boolean allaccesses) {
  92         int res = 0;
  93         res += array[i+3];
  94         if (allaccesses) {
  95             res += array[i+2];
  96             res += array[i+1];
  97             res += array[i+4];
  98             res += array[i+5];
  99             res += array[i+6];
 100             res += array[i+7];
 101             res += array[i+8];
 102             res += array[i+9];
 103         }
 104         return res;
 105     }
 106 
 107     @Args({0, -9})
 108     static int m4(int[] array, int i, boolean allaccesses) {
 109         int res = 0;
 110         res += array[i+3];
 111         if (allaccesses) {
 112             res += array[i+4];
 113             res += array[i+1];
 114             res += array[i+2];
 115             res += array[i+5];
 116             res += array[i+6];
 117             res += array[i+7];
 118             res += array[i+8];
 119             res += array[i+9];
 120         }
 121         return res;
 122     }
 123 
 124     @Args({0, -3})
 125     static int m5(int[] array, int i, boolean allaccesses) {
 126         int res = 0;
 127         res += array[i+3];
 128         res += array[i+2];
 129         if (allaccesses) {
 130             res += array[i+1];
 131             res += array[i+4];
 132             res += array[i+5];
 133             res += array[i+6];
 134             res += array[i+7];
 135             res += array[i+8];
 136             res += array[i+9];
 137         }
 138         return res;
 139     }
 140 
 141     @Args({0, 6})
 142     static int m6(int[] array, int i, boolean allaccesses) {
 143         int res = 0;
 144         res += array[i+3];
 145         res += array[i+4];
 146         if (allaccesses) {
 147             res += array[i+2];
 148             res += array[i+1];
 149             res += array[i+5];
 150             res += array[i+6];
 151             res += array[i+7];
 152             res += array[i+8];
 153             res += array[i+9];
 154         }
 155         return res;
 156     }
 157 
 158     @Args({0, 6})
 159     static int m7(int[] array, int i, boolean allaccesses) {
 160         int res = 0;
 161         res += array[i+3];
 162         res += array[i+2];
 163         res += array[i+4];
 164         if (allaccesses) {
 165             res += array[i+1];
 166             res += array[i+5];
 167             res += array[i+6];
 168             res += array[i+7];
 169             res += array[i+8];
 170             res += array[i+9];
 171         }
 172         return res;
 173     }
 174 
 175     @Args({0, -3})
 176     static int m8(int[] array, int i, boolean allaccesses) {
 177         int res = 0;
 178         res += array[i+3];
 179         res += array[i+4];
 180         res += array[i+2];
 181         if (allaccesses) {
 182             res += array[i+1];
 183             res += array[i+5];
 184             res += array[i+6];
 185             res += array[i+7];
 186             res += array[i+8];
 187             res += array[i+9];
 188         }
 189         return res;
 190     }
 191 
 192     @Args({6, 15})
 193     static int m9(int[] array, int i, boolean allaccesses) {
 194         int res = 0;
 195         res += array[i+3];
 196         if (allaccesses) {
 197             res += array[i-2];
 198             res += array[i-1];
 199             res += array[i-4];
 200             res += array[i-5];
 201             res += array[i-6];
 202         }
 203         return res;
 204     }
 205 
 206     @Args({3, 12})
 207     static int m10(int[] array, int i, boolean allaccesses) {
 208         int res = 0;
 209         res += array[i+3];
 210         if (allaccesses) {
 211             res += array[i-2];
 212             res += array[i-1];
 213             res += array[i-3];
 214             res += array[i+4];
 215             res += array[i+5];
 216             res += array[i+6];
 217         }
 218         return res;
 219     }
 220 
 221     @Args({3, -3})
 222     static int m11(int[] array, int i, boolean allaccesses) {
 223         int res = 0;
 224         res += array[i+3];
 225         res += array[i-2];
 226         if (allaccesses) {
 227             res += array[i+5];
 228             res += array[i+6];
 229         }
 230         return res;
 231     }
 232 
 233     @Args({3, 6})
 234     static int m12(int[] array, int i, boolean allaccesses) {
 235         int res = 0;
 236         res += array[i+3];
 237         res += array[i+6];
 238         if (allaccesses) {
 239             res += array[i-2];
 240             res += array[i-3];
 241         }
 242         return res;
 243     }
 244 
 245     // check that identical range check is replaced by dominating one
 246     // only when correct
 247     @Args({0})
 248     static int m13(int[] array, int i, boolean ignore) {
 249         int res = 0;
 250         res += array[i+3];
 251         res += array[i+3];
 252         return res;
 253     }
 254 
 255     @Args({2, 0})
 256     static int m14(int[] array, int i, boolean ignore) {
 257         int res = 0;
 258 
 259         res += array[i];
 260         res += array[i-2];
 261         res += array[i]; // If range check below were to be removed first this cannot be considered identical to first range check
 262         res += array[i-1]; // range check removed so i-1 array access depends on previous check
 263 
 264         return res;
 265     }
 266 
 267     static int[] m15_dummy = new int[10];
 268     @Args({2, 0})
 269     static int m15(int[] array, int i, boolean ignore) {
 270         int res = 0;
 271         res += array[i];
 272 
 273         // When the loop is optimized out we don't want the
 274         // array[i-1] access which is dependent on array[i]'s
 275         // range check to become dependent on the identical range
 276         // check above.
 277 
 278         int[] array2 = m15_dummy;
 279         int j = 0;
 280         for (; j < 10; j++);
 281         if (j == 10) {
 282             array2 = array;
 283         }
 284 
 285         res += array2[i-2];
 286         res += array2[i];
 287         res += array2[i-1]; // range check removed so i-1 array access depends on previous check
 288 
 289         return res;
 290     }
 291 
 292     @Args({2, 0})
 293     static int m16(int[] array, int i, boolean ignore) {
 294         int res = 0;
 295 
 296         res += array[i];
 297         res += array[i-1];
 298         res += array[i-1];
 299         res += array[i-2];
 300 
 301         return res;
 302     }
 303 
 304     @Args({2, 0})
 305     static int m17(int[] array, int i, boolean ignore) {
 306         int res = 0;
 307 
 308         res += array[i];
 309         res += array[i-2];
 310         res += array[i-2];
 311         res += array[i+2];
 312         res += array[i+2];
 313         res += array[i-1];
 314         res += array[i-1];
 315 
 316         return res;
 317     }
 318 
 319     static public void main(String[] args) {
 320         if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) {
 321             throw new AssertionError("Background compilation enabled");
 322         }
 323         new TestRangeCheckSmearing().doTests();
 324     }
 325     boolean success = true;
 326     boolean exception = false;
 327     final int[] array = new int[10];
 328     final HashMap<String,Method> tests = new HashMap<>();
 329     {
 330         final Class<?> TEST_PARAM_TYPES[] = { int[].class, int.class, boolean.class };
 331         for (Method m : this.getClass().getDeclaredMethods()) {
 332             if (m.getName().matches("m[0-9]+")) {
 333                 assert(Modifier.isStatic(m.getModifiers())) : m;
 334                 assert(m.getReturnType() == int.class) : m;
 335                 assert(Arrays.equals(m.getParameterTypes(), TEST_PARAM_TYPES)) : m;
 336                 tests.put(m.getName(), m);
 337             }
 338         }
 339     }
 340 
 341     void invokeTest(Method m, int[] array, int index, boolean z) {
 342         try {
 343             m.invoke(null, array, index, z);
 344         } catch (ReflectiveOperationException roe) {
 345             Throwable ex = roe.getCause();
 346             if (ex instanceof ArrayIndexOutOfBoundsException)
 347                 throw (ArrayIndexOutOfBoundsException) ex;
 348             throw new AssertionError(roe);
 349         }
 350     }
 351 
 352     void doTest(String name) {
 353         Method m = tests.get(name);
 354         tests.remove(name);
 355         int[] args = m.getAnnotation(Args.class).value();
 356         int index0 = args[0], index1;
 357         boolean exceptionRequired = true;
 358         if (args.length == 2) {
 359             index1 = args[1];
 360         } else {
 361             // no negative test for this one
 362             assert(args.length == 1);
 363             assert(name.equals("m13"));
 364             exceptionRequired = false;
 365             index1 = index0;
 366         }
 367         // Get the method compiled.
 368         if (!WHITE_BOX.isMethodCompiled(m)) {
 369             // If not, try to compile it with C2
 370             if(!WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) {
 371                 // C2 compiler not available, try to compile with C1
 372                 WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE);
 373             }
 374         }
 375         if (!WHITE_BOX.isMethodCompiled(m)) {
 376             throw new RuntimeException(m + " not compiled");
 377         }
 378 
 379         // valid access
 380         invokeTest(m, array, index0, true);
 381 
 382         if (!WHITE_BOX.isMethodCompiled(m)) {
 383             throw new RuntimeException(m + " deoptimized on valid array access");
 384         }
 385 
 386         exception = false;
 387         boolean test_success = true;
 388         try {
 389             invokeTest(m, array, index1, false);
 390         } catch(ArrayIndexOutOfBoundsException aioob) {
 391             exception = true;
 392             System.out.println("ArrayIndexOutOfBoundsException thrown in "+name);
 393         }
 394         if (!exception) {
 395             System.out.println("ArrayIndexOutOfBoundsException was not thrown in "+name);
 396         }
 397 
 398         if (Platform.isServer()) {
 399             if (exceptionRequired == WHITE_BOX.isMethodCompiled(m)) {
 400                 System.out.println((exceptionRequired?"Didn't deoptimized":"deoptimized") + " in "+name);
 401                 test_success = false;
 402             }
 403         }
 404 
 405         if (exception != exceptionRequired) {
 406             System.out.println((exceptionRequired?"exception required but not thrown":"not exception required but thrown") + " in "+name);
 407             test_success = false;
 408         }
 409 
 410         if (!test_success) {
 411             success = false;
 412             System.out.println("TEST FAILED: "+name);
 413         }
 414 
 415     }
 416     void doTests() {
 417         doTest("m1");
 418         doTest("m2");
 419         doTest("m3");
 420         doTest("m4");
 421         doTest("m5");
 422         doTest("m6");
 423         doTest("m7");
 424         doTest("m8");
 425         doTest("m9");
 426         doTest("m10");
 427         doTest("m11");
 428         doTest("m12");
 429         doTest("m13");
 430         doTest("m14");
 431         doTest("m15");
 432         doTest("m16");
 433         doTest("m17");
 434         if (!success) {
 435             throw new RuntimeException("Some tests failed");
 436         }
 437         assert(tests.isEmpty()) : tests;
 438     }
 439 }