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