TestRangeCheckSmearing.java revision 11707:ad7af1afda7a
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 * @ignore 8157984
32 * @build compiler.rangechecks.TestRangeCheckSmearing
33 * @run driver ClassFileInstaller sun.hotspot.WhiteBox
34 *                                jdk.test.lib.Platform
35 * @run main/othervm -ea -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
36 *                   -XX:-BackgroundCompilation -XX:-UseOnStackReplacement
37 *                   compiler.rangechecks.TestRangeCheckSmearing
38 *
39 */
40
41package compiler.rangechecks;
42
43import compiler.whitebox.CompilerWhiteBoxTest;
44import jdk.test.lib.Platform;
45import sun.hotspot.WhiteBox;
46
47import java.lang.annotation.Retention;
48import java.lang.annotation.RetentionPolicy;
49import java.lang.reflect.Method;
50import java.lang.reflect.Modifier;
51import java.util.Arrays;
52import java.util.HashMap;
53
54public 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 (Platform.isServer()) {
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}
447