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 }