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