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