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 }