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