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