Mercurial > hg > truffle
comparison test/compiler/rangechecks/TestRangeCheckSmearing.java @ 20707:e3d0aaab84aa
8066103: C2's range check smearing allows out of bound array accesses
Summary: range check smearing uncorrectly adjust first range check in a list of range checks to cover all of them
Reviewed-by: jrose, kvn, iveresov
author | roland |
---|---|
date | Tue, 09 Dec 2014 18:49:13 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
20706:793204f5528a | 20707:e3d0aaab84aa |
---|---|
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 } |