TestExplicitRangeChecks.java revision 11098:927d84d0b391
1/*
2 * Copyright (c) 2016, 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 8073480
27 * @summary explicit range checks should be recognized by C2
28 * @modules java.base/jdk.internal.misc
29 * @library /testlibrary /test/lib /compiler/whitebox /
30 * @build TestExplicitRangeChecks
31 * @run main ClassFileInstaller sun.hotspot.WhiteBox
32 * @run main ClassFileInstaller jdk.test.lib.Platform
33 * @run main/othervm -ea -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
34 *                   -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=compileonly,TestExplicitRangeChecks.test* TestExplicitRangeChecks
35 *
36 */
37
38import java.lang.annotation.*;
39import java.lang.reflect.*;
40import java.util.*;
41import sun.hotspot.WhiteBox;
42import sun.hotspot.code.NMethod;
43import jdk.test.lib.Platform;
44import jdk.internal.misc.Unsafe;
45import compiler.whitebox.CompilerWhiteBoxTest;
46
47public class TestExplicitRangeChecks {
48    private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
49    private static final int TIERED_STOP_AT_LEVEL = WHITE_BOX.getIntxVMFlag("TieredStopAtLevel").intValue();
50    private static int[] array = new int[10];
51    private static boolean success = true;
52
53    @Retention(RetentionPolicy.RUNTIME)
54    @interface Args {
55        int[] compile();
56        int[] good();
57        int[] bad();
58        boolean deoptimize() default true;
59    }
60
61    // Should be compiled as a single unsigned comparison
62    // 0 <= index < array.length
63    @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})
64    static boolean test1_1(int index, int[] array) {
65        if (index < 0 || index >= array.length) {
66            return false;
67        }
68        return true;
69    }
70
71    // same test but so we can compile with same optimization after trap in test1_1
72    static boolean test1_2(int index, int[] array) {
73        if (index < 0 || index >= array.length) {
74            return false;
75        }
76        return true;
77    }
78
79    // Shouldn't matter whether first or second test is the one
80    // against a constants
81    // 0 <= index < array.length
82    @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})
83    static boolean test2_1(int index, int[] array) {
84        if (index >= array.length || index < 0) {
85            return false;
86        }
87        return true;
88    }
89
90    static boolean test2_2(int index, int[] array) {
91        if (index >= array.length || index < 0) {
92            return false;
93        }
94        return true;
95    }
96
97    // 0 <= index <= array.length
98    @Args(compile = {5,}, good = {0, 10}, bad = {-1, 11})
99    static boolean test3_1(int index, int[] array) {
100        if (index < 0 || index > array.length) {
101            return false;
102        }
103        return true;
104    }
105
106    static boolean test3_2(int index, int[] array) {
107        if (index < 0 || index > array.length) {
108            return false;
109        }
110        return true;
111    }
112
113    // 0 <= index <= array.length
114    @Args(compile = {5,}, good = {0, 10}, bad = {-1, 11})
115    static boolean test4_1(int index, int[] array) {
116        if (index > array.length || index < 0 ) {
117            return false;
118        }
119        return true;
120    }
121
122    static boolean test4_2(int index, int[] array) {
123        if (index > array.length || index < 0) {
124            return false;
125        }
126        return true;
127    }
128
129    static int[] test5_helper(int i) {
130        return (i < 100) ? new int[10] : new int[5];
131    }
132
133    // 0 < index < array.length
134    @Args(compile = {5,}, good = {1, 9}, bad = {0, 10})
135    static boolean test5_1(int index, int[] array) {
136        array = test5_helper(index); // array.length must be not constant greater than 1
137        if (index <= 0 || index >= array.length) {
138            return false;
139        }
140        return true;
141    }
142
143    static boolean test5_2(int index, int[] array) {
144        array = test5_helper(index); // array.length must be not constant greater than 1
145        if (index <= 0 || index >= array.length) {
146            return false;
147        }
148        return true;
149    }
150
151    // 0 < index < array.length
152    @Args(compile = {5,}, good = {1, 9}, bad = {0, 10})
153    static boolean test6_1(int index, int[] array) {
154        array = test5_helper(index); // array.length must be not constant greater than 1
155        if (index >= array.length || index <= 0 ) {
156            return false;
157        }
158        return true;
159    }
160
161    static boolean test6_2(int index, int[] array) {
162        array = test5_helper(index); // array.length must be not constant greater than 1
163        if (index >= array.length || index <= 0) {
164            return false;
165        }
166        return true;
167    }
168
169    // 0 < index <= array.length
170    @Args(compile = {5,}, good = {1, 10}, bad = {0, 11})
171    static boolean test7_1(int index, int[] array) {
172        if (index <= 0 || index > array.length) {
173            return false;
174        }
175        return true;
176    }
177
178    static boolean test7_2(int index, int[] array) {
179        if (index <= 0 || index > array.length) {
180            return false;
181        }
182        return true;
183    }
184
185    // 0 < index <= array.length
186    @Args(compile = {5,}, good = {1, 10}, bad = {0, 11})
187    static boolean test8_1(int index, int[] array) {
188        if (index > array.length || index <= 0 ) {
189            return false;
190        }
191        return true;
192    }
193
194    static boolean test8_2(int index, int[] array) {
195        if (index > array.length || index <= 0) {
196            return false;
197        }
198        return true;
199    }
200
201    static int[] test9_helper1(int i) {
202        return (i < 100) ? new int[1] : new int[2];
203    }
204
205    static int[] test9_helper2(int i) {
206        return (i < 100) ? new int[10] : new int[11];
207    }
208
209    // array1.length <= index < array2.length
210    @Args(compile = {5,}, good = {1, 9}, bad = {0, 10})
211    static boolean test9_1(int index, int[] array) {
212        int[] array1 = test9_helper1(index);
213        int[] array2 = test9_helper2(index);
214        if (index < array1.length || index >= array2.length) {
215            return false;
216        }
217        return true;
218    }
219
220    static boolean test9_2(int index, int[] array) {
221        int[] array1 = test9_helper1(index);
222        int[] array2 = test9_helper2(index);
223        if (index < array1.length || index >= array2.length) {
224            return false;
225        }
226        return true;
227    }
228
229    // Previously supported pattern
230    @Args(compile = {-5,5,15}, good = {0, 9}, bad = {-1, 10}, deoptimize=false)
231    static boolean test10_1(int index, int[] array) {
232        if (index < 0 || index >= 10) {
233            return false;
234        }
235        return true;
236    }
237
238    static int[] array11 = new int[10];
239    @Args(compile = {5,}, good = {0, 9}, bad = {-1,})
240    static boolean test11_1(int index, int[] array) {
241        if (index < 0) {
242            return false;
243        }
244        int unused = array11[index];
245        // If this one is folded with the first test then we allow
246        // array access above to proceed even for out of bound array
247        // index and the method throws an
248        // ArrayIndexOutOfBoundsException.
249        if (index >= array.length) {
250            return false;
251        }
252        return true;
253    }
254
255    static int[] array12 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
256    @Args(compile = {5,}, good = {0, 9}, bad = {-1,})
257    static boolean test12_1(int index, int[] array) {
258        // Cannot be folded otherwise would cause incorrect array
259        // access if the array12 range check is executed before the
260        // folded test.
261        if (index < 0 || index >= array12[index]) {
262            return false;
263        }
264        return true;
265    }
266
267    // Same as test1_1 but pass null array when index < 0: shouldn't
268    // cause NPE.
269    @Args(compile = {5,}, good = {0, 9}, bad = {})
270    static boolean test13_1(int index, int[] array) {
271        if (index < 0 || index >= array.length) {
272            return false;
273        }
274        return true;
275    }
276
277    // Same as test10 but with uncommon traps
278    @Args(compile = {5}, good = {0, 9}, bad = {-1, 10})
279    static boolean test14_1(int index, int[] array) {
280        if (index < 0 || index >= 10) {
281            return false;
282        }
283        return true;
284    }
285
286    static boolean test14_2(int index, int[] array) {
287        if (index < 0 || index >= 10) {
288            return false;
289        }
290        return true;
291    }
292
293    // Same as test13_1 but pass null array: null trap should be reported on first if
294    @Args(compile = {5,}, good = {0, 9}, bad = {})
295    static boolean test15_1(int index, int[] array) {
296        if (index < 0 || index >= array.length) {
297            return false;
298        }
299        return true;
300    }
301
302    // Same as test1 but with no null check between the integer comparisons
303    @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})
304    static boolean test16_1(int index, int[] array) {
305        int l = array.length;
306        if (index < 0 || index >= l) {
307            return false;
308        }
309        return true;
310    }
311
312    static boolean test16_2(int index, int[] array) {
313        int l = array.length;
314        if (index < 0 || index >= l) {
315            return false;
316        }
317        return true;
318    }
319
320    // Same as test1 but bound check on array access should optimize
321    // out.
322    @Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})
323    static boolean test17_1(int index, int[] array) {
324        if (index < 0 || index >= array.length) {
325            return false;
326        }
327        array[index] = 0;
328        return true;
329    }
330
331    static boolean test17_2(int index, int[] array) {
332        if (index < 0 || index >= array.length) {
333            return false;
334        }
335        array[index] = 0;
336        return true;
337    }
338
339    // Same as test1 but range check smearing should optimize
340    // 3rd range check out.
341    @Args(compile = {5,}, good = {}, bad = {})
342    static boolean test18_1(int index, int[] array) {
343        if (index < 0 || index >= array.length) {
344            return false;
345        }
346        array[index+2] = 0;
347        array[index+1] = 0;
348        return true;
349    }
350
351    static boolean test19_helper1(int index) {
352        if (index < 12) {
353            return false;
354        }
355        return true;
356    }
357
358    static boolean test19_helper2(int index) {
359        if (index > 8) {
360            return false;
361        }
362        return true;
363    }
364
365    // Second test should be optimized out
366    static boolean test19(int index, int[] array) {
367        test19_helper1(index);
368        test19_helper2(index);
369        return true;
370    }
371
372    final HashMap<String,Method> tests = new HashMap<>();
373    {
374        for (Method m : this.getClass().getDeclaredMethods()) {
375            if (m.getName().matches("test[0-9]+(_[0-9])?")) {
376                assert(Modifier.isStatic(m.getModifiers())) : m;
377                tests.put(m.getName(), m);
378            }
379        }
380    }
381
382    void doTest(String name) throws Exception {
383        Method m = tests.get(name + "_1");
384
385        Args anno =  m.getAnnotation(Args.class);
386        int[] compile = anno.compile();
387        int[] good = anno.good();
388        int[] bad = anno.bad();
389        boolean deoptimize = anno.deoptimize();
390
391        // Get compiled
392        for (int i = 0; i < 20000;) {
393            for (int j = 0; j < compile.length; j++) {
394                m.invoke(null, compile[j], array);
395                i++;
396            }
397        }
398
399        if (!WHITE_BOX.isMethodCompiled(m)) {
400            System.out.println(name + "_1 not compiled");
401            success = false;
402        }
403
404        // check that good values don't trigger exception or
405        // deoptimization
406        for (int i = 0; i < good.length; i++) {
407            boolean res = (boolean)m.invoke(null, good[i], array);
408
409            if (!res) {
410                System.out.println(name + " bad result for good input " + good[i]);
411                success = false;
412            }
413            if (!WHITE_BOX.isMethodCompiled(m)) {
414                System.out.println(name + " deoptimized on valid access");
415                success = false;
416            }
417        }
418
419        // check that bad values trigger exception and deoptimization
420        for (int i = 0; i < bad.length; i++) {
421            if (i > 0 && deoptimize) {
422                m = tests.get(name + "_" + (i+1));
423                for (int k = 0; k < 20000;) {
424                    for (int j = 0; j < compile.length; j++) {
425                        m.invoke(null, compile[j], array);
426                        k++;
427                    }
428                }
429                if (!WHITE_BOX.isMethodCompiled(m)) {
430                    System.out.println(name + ("_" + (i+1)) + " not compiled");
431                    success = false;
432                }
433            }
434
435            boolean res = (boolean)m.invoke(null, bad[i], array);
436
437            if (res) {
438                System.out.println(name + " bad result for bad input " + bad[i]);
439                success = false;
440            }
441            // Only perform these additional checks if C2 is available
442            if (Platform.isServer() &&
443                TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) {
444                if (deoptimize && WHITE_BOX.isMethodCompiled(m)) {
445                    System.out.println(name + " not deoptimized on invalid access");
446                    success = false;
447                } else if (!deoptimize && !WHITE_BOX.isMethodCompiled(m)) {
448                    System.out.println(name + " deoptimized on invalid access");
449                    success = false;
450                }
451            }
452        }
453
454    }
455
456    private static final Unsafe UNSAFE;
457
458    static {
459        try {
460            Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
461            unsafeField.setAccessible(true);
462            UNSAFE = (Unsafe) unsafeField.get(null);
463        }
464        catch (Exception e) {
465            throw new AssertionError(e);
466        }
467    }
468
469    // On x64, int to long conversion should optimize away in address computation
470    static int test20(int[] a) {
471        int sum = 0;
472        for (int i = 0; i < a.length; i++) {
473            sum += test20_helper(a, i);
474        }
475        return sum;
476    }
477
478    static int test20_helper(int[] a, int i) {
479        if (i < 0 || i >= a.length)
480            throw new ArrayIndexOutOfBoundsException();
481
482        long address = (((long) i) << 2) + UNSAFE.ARRAY_INT_BASE_OFFSET;
483        return UNSAFE.getInt(a, address);
484    }
485
486    static int test21(int[] a) {
487        int sum = 0;
488        for (int i = 0; i < a.length; i++) {
489            sum += test20_helper(a, i);
490        }
491        return sum;
492    }
493
494    static int test21_helper(int[] a, int i) {
495        if (i < 0 || i >= a.length)
496            throw new ArrayIndexOutOfBoundsException();
497
498        long address = (((long) i) << 2) + UNSAFE.ARRAY_INT_BASE_OFFSET;
499        return UNSAFE.getIntVolatile(a, address);
500    }
501
502    static public void main(String[] args) throws Exception {
503
504        if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) {
505            throw new AssertionError("Background compilation enabled");
506        }
507
508        TestExplicitRangeChecks test = new TestExplicitRangeChecks();
509
510        test.doTest("test1");
511        test.doTest("test2");
512        test.doTest("test3");
513        test.doTest("test4");
514
515        // pollute branch profile
516        for (int i = 0; i < 10000; i++) {
517            test5_helper((i%2 == 0) ? 0 : 1000);
518        }
519
520        test.doTest("test5");
521        test.doTest("test6");
522        test.doTest("test7");
523        test.doTest("test8");
524
525        // pollute branch profile
526        for (int i = 0; i < 10000; i++) {
527            test9_helper1((i%2 == 0) ? 0 : 1000);
528            test9_helper2((i%2 == 0) ? 0 : 1000);
529        }
530
531        test.doTest("test9");
532        test.doTest("test10");
533        test.doTest("test11");
534        test.doTest("test12");
535
536        test.doTest("test13");
537        {
538            Method m = test.tests.get("test13_1");
539            for (int i = 0; i < 1; i++) {
540                test13_1(-1, null);
541                if (!WHITE_BOX.isMethodCompiled(m)) {
542                    break;
543                }
544            }
545        }
546        test.doTest("test13");
547        {
548            Method m = test.tests.get("test13_1");
549            for (int i = 0; i < 10; i++) {
550                test13_1(-1, null);
551                if (!WHITE_BOX.isMethodCompiled(m)) {
552                    break;
553                }
554            }
555        }
556
557        test.doTest("test14");
558
559        test.doTest("test15");
560        {
561            Method m = test.tests.get("test15_1");
562            for (int i = 0; i < 10; i++) {
563                try {
564                    test15_1(5, null);
565                } catch(NullPointerException npe) {}
566                if (!WHITE_BOX.isMethodCompiled(m)) {
567                    break;
568                }
569            }
570        }
571        test.doTest("test15");
572        test.doTest("test16");
573        test.doTest("test17");
574        test.doTest("test18");
575
576        for (int i = 0; i < 20000; i++) {
577            test19_helper1(20);
578            test19_helper2(5);
579        }
580
581        {
582            Method m = test.tests.get("test19");
583            WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
584        }
585
586        for (int i = 0; i < 20000; i++) {
587            test20(array);
588        }
589
590        for (int i = 0; i < 20000; i++) {
591            test21(array);
592        }
593
594        if (!success) {
595            throw new RuntimeException("some tests failed");
596        }
597    }
598}
599