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