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