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