1/*
2 * Copyright (c) 2011, 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 */
23package org.graalvm.compiler.core.test.ea;
24
25import jdk.vm.ci.meta.JavaConstant;
26
27import org.junit.Assert;
28import org.junit.Test;
29
30import org.graalvm.compiler.graph.Node;
31import org.graalvm.compiler.loop.DefaultLoopPolicies;
32import org.graalvm.compiler.loop.phases.LoopFullUnrollPhase;
33import org.graalvm.compiler.loop.phases.LoopPeelingPhase;
34import org.graalvm.compiler.nodes.extended.ValueAnchorNode;
35import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
36import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
37import org.graalvm.compiler.phases.common.CanonicalizerPhase;
38import org.graalvm.compiler.phases.schedule.SchedulePhase;
39import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
40
41/**
42 * The PartialEscapeAnalysisPhase is expected to remove all allocations and return the correct
43 * values.
44 */
45public class EscapeAnalysisTest extends EATestBase {
46
47    @Test
48    public void test1() {
49        testEscapeAnalysis("test1Snippet", JavaConstant.forInt(101), false);
50    }
51
52    public static int test1Snippet() {
53        Integer x = new Integer(101);
54        return x.intValue();
55    }
56
57    @Test
58    public void test2() {
59        testEscapeAnalysis("test2Snippet", JavaConstant.forInt(0), false);
60    }
61
62    public static int test2Snippet() {
63        Integer[] x = new Integer[0];
64        return x.length;
65    }
66
67    @Test
68    public void test3() {
69        testEscapeAnalysis("test3Snippet", JavaConstant.NULL_POINTER, false);
70    }
71
72    public static Object test3Snippet() {
73        Integer[] x = new Integer[1];
74        return x[0];
75    }
76
77    @Test
78    public void testMonitor() {
79        testEscapeAnalysis("testMonitorSnippet", JavaConstant.forInt(0), false);
80    }
81
82    public static int testMonitorSnippet() {
83        Integer x = new Integer(0);
84        Double y = new Double(0);
85        Object z = new Object();
86        synchronized (x) {
87            synchronized (y) {
88                synchronized (z) {
89                    notInlineable();
90                }
91            }
92        }
93        return x.intValue();
94    }
95
96    @Test
97    public void testMonitor2() {
98        testEscapeAnalysis("testMonitor2Snippet", JavaConstant.forInt(0), false);
99    }
100
101    /**
102     * This test case differs from the last one in that it requires inlining within a synchronized
103     * region.
104     */
105    public static int testMonitor2Snippet() {
106        Integer x = new Integer(0);
107        Double y = new Double(0);
108        Object z = new Object();
109        synchronized (x) {
110            synchronized (y) {
111                synchronized (z) {
112                    notInlineable();
113                    return x.intValue();
114                }
115            }
116        }
117    }
118
119    @Test
120    public void testMerge() {
121        testEscapeAnalysis("testMerge1Snippet", JavaConstant.forInt(0), true);
122    }
123
124    public static int testMerge1Snippet(int a) {
125        TestClassInt obj = new TestClassInt(1, 0);
126        if (a < 0) {
127            obj.x = obj.x + 1;
128        } else {
129            obj.x = obj.x + 2;
130            obj.y = 0;
131        }
132        if (obj.x > 1000) {
133            return 1;
134        }
135        return obj.y;
136    }
137
138    @Test
139    public void testSimpleLoop() {
140        testEscapeAnalysis("testSimpleLoopSnippet", JavaConstant.forInt(1), false);
141    }
142
143    public int testSimpleLoopSnippet(int a) {
144        TestClassInt obj = new TestClassInt(1, 2);
145        for (int i = 0; i < a; i++) {
146            notInlineable();
147        }
148        return obj.x;
149    }
150
151    @Test
152    public void testModifyingLoop() {
153        testEscapeAnalysis("testModifyingLoopSnippet", JavaConstant.forInt(1), false);
154    }
155
156    public int testModifyingLoopSnippet(int a) {
157        TestClassInt obj = new TestClassInt(1, 2);
158        for (int i = 0; i < a; i++) {
159            obj.x = 3;
160            notInlineable();
161        }
162        return obj.x <= 3 ? 1 : 0;
163    }
164
165    @Test
166    public void testMergeAllocationsInt() {
167        testEscapeAnalysis("testMergeAllocationsIntSnippet", JavaConstant.forInt(1), false);
168    }
169
170    public int testMergeAllocationsIntSnippet(int a) {
171        TestClassInt obj;
172        if (a < 0) {
173            obj = new TestClassInt(1, 2);
174            notInlineable();
175        } else {
176            obj = new TestClassInt(1, 2);
177            notInlineable();
178        }
179        return obj.x <= 3 ? 1 : 0;
180    }
181
182    @Test
183    public void testMergeAllocationsObj() {
184        testEscapeAnalysis("testMergeAllocationsObjSnippet", JavaConstant.forInt(1), false);
185    }
186
187    public int testMergeAllocationsObjSnippet(int a) {
188        TestClassObject obj;
189        Integer one = 1;
190        Integer two = 2;
191        Integer three = 3;
192        if (a < 0) {
193            obj = new TestClassObject(one, two);
194            notInlineable();
195        } else {
196            obj = new TestClassObject(one, three);
197            notInlineable();
198        }
199        return ((Integer) obj.x).intValue() <= 3 ? 1 : 0;
200    }
201
202    @Test
203    public void testMergeAllocationsObjCirc() {
204        testEscapeAnalysis("testMergeAllocationsObjCircSnippet", JavaConstant.forInt(1), false);
205    }
206
207    public int testMergeAllocationsObjCircSnippet(int a) {
208        TestClassObject obj;
209        Integer one = 1;
210        Integer two = 2;
211        Integer three = 3;
212        if (a < 0) {
213            obj = new TestClassObject(one);
214            obj.y = obj;
215            obj.y = two;
216            notInlineable();
217        } else {
218            obj = new TestClassObject(one);
219            obj.y = obj;
220            obj.y = three;
221            notInlineable();
222        }
223        return ((Integer) obj.x).intValue() <= 3 ? 1 : 0;
224    }
225
226    static class MyException extends RuntimeException {
227
228        private static final long serialVersionUID = 0L;
229
230        protected Integer value;
231
232        MyException(Integer value) {
233            super((Throwable) null);
234            this.value = value;
235        }
236
237        @SuppressWarnings("sync-override")
238        @Override
239        public final Throwable fillInStackTrace() {
240            return null;
241        }
242    }
243
244    @Test
245    public void testMergeAllocationsException() {
246        testEscapeAnalysis("testMergeAllocationsExceptionSnippet", JavaConstant.forInt(1), false);
247    }
248
249    public int testMergeAllocationsExceptionSnippet(int a) {
250        MyException obj;
251        Integer one = 1;
252        if (a < 0) {
253            obj = new MyException(one);
254            notInlineable();
255        } else {
256            obj = new MyException(one);
257            notInlineable();
258        }
259        return obj.value <= 3 ? 1 : 0;
260    }
261
262    @Test
263    public void testCheckCast() {
264        testEscapeAnalysis("testCheckCastSnippet", getSnippetReflection().forObject(TestClassObject.class), false);
265    }
266
267    public Object testCheckCastSnippet() {
268        TestClassObject obj = new TestClassObject(TestClassObject.class);
269        TestClassObject obj2 = new TestClassObject(obj);
270        return ((TestClassObject) obj2.x).x;
271    }
272
273    @Test
274    public void testInstanceOf() {
275        testEscapeAnalysis("testInstanceOfSnippet", JavaConstant.forInt(1), false);
276    }
277
278    public boolean testInstanceOfSnippet() {
279        TestClassObject obj = new TestClassObject(TestClassObject.class);
280        TestClassObject obj2 = new TestClassObject(obj);
281        return obj2.x instanceof TestClassObject;
282    }
283
284    @SuppressWarnings("unused")
285    public static void testNewNodeSnippet() {
286        new ValueAnchorNode(null);
287    }
288
289    /**
290     * This test makes sure that the allocation of a {@link Node} can be removed. It therefore also
291     * tests the intrinsification of {@link Object#getClass()}.
292     */
293    @Test
294    public void testNewNode() {
295        testEscapeAnalysis("testNewNodeSnippet", null, false);
296    }
297
298    private static final TestClassObject staticObj = new TestClassObject();
299
300    public static Object testFullyUnrolledLoopSnippet() {
301        /*
302         * This tests a case that can appear if PEA is performed both before and after loop
303         * unrolling/peeling: If the VirtualInstanceNode is not duplicated correctly with the loop,
304         * the resulting object will reference itself, and not a second (different) object.
305         */
306        TestClassObject obj = staticObj;
307        for (int i = 0; i < 2; i++) {
308            obj = new TestClassObject(obj);
309        }
310        return obj.x;
311    }
312
313    @Test
314    public void testFullyUnrolledLoop() {
315        prepareGraph("testFullyUnrolledLoopSnippet", false);
316        new LoopFullUnrollPhase(new CanonicalizerPhase(), new DefaultLoopPolicies()).apply(graph, context);
317        new PartialEscapePhase(false, new CanonicalizerPhase()).apply(graph, context);
318        Assert.assertEquals(1, returnNodes.size());
319        Assert.assertTrue(returnNodes.get(0).result() instanceof AllocatedObjectNode);
320        CommitAllocationNode commit = ((AllocatedObjectNode) returnNodes.get(0).result()).getCommit();
321        Assert.assertEquals(2, commit.getValues().size());
322        Assert.assertEquals(1, commit.getVirtualObjects().size());
323        Assert.assertTrue("non-cyclic data structure expected", commit.getVirtualObjects().get(0) != commit.getValues().get(0));
324    }
325
326    @SuppressWarnings("unused") private static Object staticField;
327
328    private static TestClassObject inlinedPart(TestClassObject obj) {
329        TestClassObject ret = new TestClassObject(obj);
330        staticField = null;
331        return ret;
332    }
333
334    public static Object testPeeledLoopSnippet() {
335        TestClassObject obj = staticObj;
336        int i = 0;
337        do {
338            obj = inlinedPart(obj);
339        } while (i++ < 10);
340        staticField = obj;
341        return obj.x;
342    }
343
344    @Test
345    public void testPeeledLoop() {
346        prepareGraph("testPeeledLoopSnippet", false);
347        new LoopPeelingPhase(new DefaultLoopPolicies()).apply(graph, getDefaultHighTierContext());
348        new SchedulePhase().apply(graph);
349    }
350
351    public static void testDeoptMonitorSnippetInner(Object o2, Object t, int i) {
352        staticField = null;
353        if (i == 0) {
354            staticField = o2;
355            Number n = (Number) t;
356            n.toString();
357        }
358    }
359
360    public static void testDeoptMonitorSnippet(Object t, int i) {
361        TestClassObject o = new TestClassObject();
362        TestClassObject o2 = new TestClassObject(o);
363
364        synchronized (o) {
365            testDeoptMonitorSnippetInner(o2, t, i);
366        }
367    }
368
369    @Test
370    public void testDeoptMonitor() {
371        test("testDeoptMonitorSnippet", new Object(), 0);
372    }
373}
374