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