001/*
002 * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.compiler.test.ea;
024
025import jdk.internal.jvmci.meta.*;
026
027import org.junit.*;
028
029import com.oracle.graal.graph.*;
030import com.oracle.graal.loop.phases.*;
031import com.oracle.graal.nodes.extended.*;
032import com.oracle.graal.nodes.virtual.*;
033import com.oracle.graal.phases.common.*;
034import com.oracle.graal.phases.schedule.*;
035import com.oracle.graal.virtual.phases.ea.*;
036
037/**
038 * The PartialEscapeAnalysisPhase is expected to remove all allocations and return the correct
039 * values.
040 */
041public class EscapeAnalysisTest extends EATestBase {
042
043    @Test
044    public void test1() {
045        testEscapeAnalysis("test1Snippet", JavaConstant.forInt(101), false);
046    }
047
048    public static int test1Snippet() {
049        Integer x = new Integer(101);
050        return x.intValue();
051    }
052
053    @Test
054    public void test2() {
055        testEscapeAnalysis("test2Snippet", JavaConstant.forInt(0), false);
056    }
057
058    public static int test2Snippet() {
059        Integer[] x = new Integer[0];
060        return x.length;
061    }
062
063    @Test
064    public void test3() {
065        testEscapeAnalysis("test3Snippet", JavaConstant.NULL_POINTER, false);
066    }
067
068    public static Object test3Snippet() {
069        Integer[] x = new Integer[1];
070        return x[0];
071    }
072
073    @Test
074    public void testMonitor() {
075        testEscapeAnalysis("testMonitorSnippet", JavaConstant.forInt(0), false);
076    }
077
078    public static int testMonitorSnippet() {
079        Integer x = new Integer(0);
080        Double y = new Double(0);
081        Object z = new Object();
082        synchronized (x) {
083            synchronized (y) {
084                synchronized (z) {
085                    notInlineable();
086                }
087            }
088        }
089        return x.intValue();
090    }
091
092    @Test
093    public void testMonitor2() {
094        testEscapeAnalysis("testMonitor2Snippet", JavaConstant.forInt(0), false);
095    }
096
097    /**
098     * This test case differs from the last one in that it requires inlining within a synchronized
099     * region.
100     */
101    public static int testMonitor2Snippet() {
102        Integer x = new Integer(0);
103        Double y = new Double(0);
104        Object z = new Object();
105        synchronized (x) {
106            synchronized (y) {
107                synchronized (z) {
108                    notInlineable();
109                    return x.intValue();
110                }
111            }
112        }
113    }
114
115    @Test
116    public void testMerge() {
117        testEscapeAnalysis("testMerge1Snippet", JavaConstant.forInt(0), true);
118    }
119
120    public static int testMerge1Snippet(int a) {
121        TestClassInt obj = new TestClassInt(1, 0);
122        if (a < 0) {
123            obj.x = obj.x + 1;
124        } else {
125            obj.x = obj.x + 2;
126            obj.y = 0;
127        }
128        if (obj.x > 1000) {
129            return 1;
130        }
131        return obj.y;
132    }
133
134    @Test
135    public void testSimpleLoop() {
136        testEscapeAnalysis("testSimpleLoopSnippet", JavaConstant.forInt(1), false);
137    }
138
139    public int testSimpleLoopSnippet(int a) {
140        TestClassInt obj = new TestClassInt(1, 2);
141        for (int i = 0; i < a; i++) {
142            notInlineable();
143        }
144        return obj.x;
145    }
146
147    @Test
148    public void testModifyingLoop() {
149        testEscapeAnalysis("testModifyingLoopSnippet", JavaConstant.forInt(1), false);
150    }
151
152    public int testModifyingLoopSnippet(int a) {
153        TestClassInt obj = new TestClassInt(1, 2);
154        for (int i = 0; i < a; i++) {
155            obj.x = 3;
156            notInlineable();
157        }
158        return obj.x <= 3 ? 1 : 0;
159    }
160
161    @Test
162    public void testMergeAllocationsInt() {
163        testEscapeAnalysis("testMergeAllocationsIntSnippet", JavaConstant.forInt(1), false);
164    }
165
166    public int testMergeAllocationsIntSnippet(int a) {
167        TestClassInt obj;
168        if (a < 0) {
169            obj = new TestClassInt(1, 2);
170            notInlineable();
171        } else {
172            obj = new TestClassInt(1, 2);
173            notInlineable();
174        }
175        return obj.x <= 3 ? 1 : 0;
176    }
177
178    @Test
179    public void testMergeAllocationsObj() {
180        testEscapeAnalysis("testMergeAllocationsObjSnippet", JavaConstant.forInt(1), false);
181    }
182
183    public int testMergeAllocationsObjSnippet(int a) {
184        TestClassObject obj;
185        Integer one = 1;
186        Integer two = 2;
187        Integer three = 3;
188        if (a < 0) {
189            obj = new TestClassObject(one, two);
190            notInlineable();
191        } else {
192            obj = new TestClassObject(one, three);
193            notInlineable();
194        }
195        return ((Integer) obj.x).intValue() <= 3 ? 1 : 0;
196    }
197
198    @Test
199    public void testMergeAllocationsObjCirc() {
200        testEscapeAnalysis("testMergeAllocationsObjCircSnippet", JavaConstant.forInt(1), false);
201    }
202
203    public int testMergeAllocationsObjCircSnippet(int a) {
204        TestClassObject obj;
205        Integer one = 1;
206        Integer two = 2;
207        Integer three = 3;
208        if (a < 0) {
209            obj = new TestClassObject(one);
210            obj.y = obj;
211            obj.y = two;
212            notInlineable();
213        } else {
214            obj = new TestClassObject(one);
215            obj.y = obj;
216            obj.y = three;
217            notInlineable();
218        }
219        return ((Integer) obj.x).intValue() <= 3 ? 1 : 0;
220    }
221
222    static class MyException extends RuntimeException {
223
224        private static final long serialVersionUID = 0L;
225
226        protected Integer value;
227
228        public MyException(Integer value) {
229            super((Throwable) null);
230            this.value = value;
231        }
232
233        @SuppressWarnings("sync-override")
234        @Override
235        public final Throwable fillInStackTrace() {
236            return null;
237        }
238    }
239
240    @Test
241    public void testMergeAllocationsException() {
242        testEscapeAnalysis("testMergeAllocationsExceptionSnippet", JavaConstant.forInt(1), false);
243    }
244
245    public int testMergeAllocationsExceptionSnippet(int a) {
246        MyException obj;
247        Integer one = 1;
248        if (a < 0) {
249            obj = new MyException(one);
250            notInlineable();
251        } else {
252            obj = new MyException(one);
253            notInlineable();
254        }
255        return obj.value <= 3 ? 1 : 0;
256    }
257
258    @Test
259    public void testCheckCast() {
260        testEscapeAnalysis("testCheckCastSnippet", getSnippetReflection().forObject(TestClassObject.class), false);
261    }
262
263    public Object testCheckCastSnippet() {
264        TestClassObject obj = new TestClassObject(TestClassObject.class);
265        TestClassObject obj2 = new TestClassObject(obj);
266        return ((TestClassObject) obj2.x).x;
267    }
268
269    @Test
270    public void testInstanceOf() {
271        testEscapeAnalysis("testInstanceOfSnippet", JavaConstant.forInt(1), false);
272    }
273
274    public boolean testInstanceOfSnippet() {
275        TestClassObject obj = new TestClassObject(TestClassObject.class);
276        TestClassObject obj2 = new TestClassObject(obj);
277        return obj2.x instanceof TestClassObject;
278    }
279
280    @SuppressWarnings("unused")
281    public static void testNewNodeSnippet() {
282        new ValueAnchorNode(null);
283    }
284
285    /**
286     * This test makes sure that the allocation of a {@link Node} can be removed. It therefore also
287     * tests the intrinsification of {@link Object#getClass()}.
288     */
289    @Test
290    public void testNewNode() {
291        testEscapeAnalysis("testNewNodeSnippet", null, false);
292    }
293
294    private static final TestClassObject staticObj = new TestClassObject();
295
296    public static Object testFullyUnrolledLoopSnippet() {
297        /*
298         * This tests a case that can appear if PEA is performed both before and after loop
299         * unrolling/peeling: If the VirtualInstanceNode is not duplicated correctly with the loop,
300         * the resulting object will reference itself, and not a second (different) object.
301         */
302        TestClassObject obj = staticObj;
303        for (int i = 0; i < 2; i++) {
304            obj = new TestClassObject(obj);
305        }
306        return obj.x;
307    }
308
309    @Test
310    public void testFullyUnrolledLoop() {
311        prepareGraph("testFullyUnrolledLoopSnippet", false);
312        new LoopFullUnrollPhase(new CanonicalizerPhase()).apply(graph, context);
313        new PartialEscapePhase(false, new CanonicalizerPhase()).apply(graph, context);
314        Assert.assertEquals(1, returnNodes.size());
315        Assert.assertTrue(returnNodes.get(0).result() instanceof AllocatedObjectNode);
316        CommitAllocationNode commit = ((AllocatedObjectNode) returnNodes.get(0).result()).getCommit();
317        Assert.assertEquals(2, commit.getValues().size());
318        Assert.assertEquals(1, commit.getVirtualObjects().size());
319        Assert.assertTrue("non-cyclic data structure expected", commit.getVirtualObjects().get(0) != commit.getValues().get(0));
320    }
321
322    @SuppressWarnings("unused") private static Object staticField;
323
324    private static TestClassObject inlinedPart(TestClassObject obj) {
325        TestClassObject ret = new TestClassObject(obj);
326        staticField = null;
327        return ret;
328    }
329
330    public static Object testPeeledLoopSnippet() {
331        TestClassObject obj = staticObj;
332        int i = 0;
333        do {
334            obj = inlinedPart(obj);
335        } while (i++ < 10);
336        staticField = obj;
337        return obj.x;
338    }
339
340    @Test
341    public void testPeeledLoop() {
342        prepareGraph("testPeeledLoopSnippet", false);
343        new LoopPeelingPhase().apply(graph);
344        new SchedulePhase().apply(graph);
345    }
346
347    public static void testDeoptMonitorSnippetInner(Object o2, Object t, int i) {
348        staticField = null;
349        if (i == 0) {
350            staticField = o2;
351            Number n = (Number) t;
352            n.toString();
353        }
354    }
355
356    public static void testDeoptMonitorSnippet(Object t, int i) {
357        TestClassObject o = new TestClassObject();
358        TestClassObject o2 = new TestClassObject(o);
359
360        synchronized (o) {
361            testDeoptMonitorSnippetInner(o2, t, i);
362        }
363    }
364
365    @Test
366    public void testDeoptMonitor() {
367        test("testDeoptMonitorSnippet", new Object(), 0);
368    }
369}