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 java.util.List;
26
27import org.graalvm.compiler.core.test.GraalCompilerTest;
28import org.graalvm.compiler.debug.DebugContext;
29import org.graalvm.compiler.nodes.ReturnNode;
30import org.graalvm.compiler.nodes.StructuredGraph;
31import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
32import org.graalvm.compiler.nodes.java.NewArrayNode;
33import org.graalvm.compiler.nodes.java.NewInstanceNode;
34import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
35import org.graalvm.compiler.phases.common.CanonicalizerPhase;
36import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
37import org.graalvm.compiler.phases.common.inlining.InliningPhase;
38import org.graalvm.compiler.phases.tiers.HighTierContext;
39import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
40import org.junit.Assert;
41
42import jdk.vm.ci.meta.JavaConstant;
43import jdk.vm.ci.meta.ResolvedJavaMethod;
44
45//JaCoCo Exclude
46
47/**
48 * This base class for all Escape Analysis tests does not contain tests itself, therefore it is not
49 * automatically excluded from JaCoCo. Since it includes code that is used in the test snippets, it
50 * needs to be excluded manually.
51 */
52public class EATestBase extends GraalCompilerTest {
53
54    public static class TestClassInt {
55        public int x;
56        public int y;
57        public int z;
58
59        public TestClassInt() {
60            this(0, 0);
61        }
62
63        public TestClassInt(int x) {
64            this(x, 0);
65        }
66
67        public TestClassInt(int x, int y) {
68            this.x = x;
69            this.y = y;
70        }
71
72        @Override
73        public boolean equals(Object obj) {
74            TestClassInt other = (TestClassInt) obj;
75            return x == other.x && y == other.y && z == other.z;
76        }
77
78        @Override
79        public String toString() {
80            return "{" + x + "," + y + "}";
81        }
82
83        @Override
84        public int hashCode() {
85            return x + 13 * y;
86        }
87    }
88
89    public static class TestClassObject {
90        public Object x;
91        public Object y;
92
93        public TestClassObject() {
94            this(null, null);
95        }
96
97        public TestClassObject(Object x) {
98            this(x, null);
99        }
100
101        public TestClassObject(Object x, Object y) {
102            this.x = x;
103            this.y = y;
104        }
105
106        @Override
107        public boolean equals(Object obj) {
108            TestClassObject other = (TestClassObject) obj;
109            return x == other.x && y == other.y;
110        }
111
112        @Override
113        public String toString() {
114            return "{" + x + "," + y + "}";
115        }
116
117        @Override
118        public int hashCode() {
119            return (x == null ? 0 : x.hashCode()) + 13 * (y == null ? 0 : y.hashCode());
120        }
121    }
122
123    protected static native void notInlineable();
124
125    protected StructuredGraph graph;
126    protected HighTierContext context;
127    protected List<ReturnNode> returnNodes;
128
129    /**
130     * Runs Escape Analysis on the given snippet and makes sure that no allocations remain in the
131     * graph.
132     *
133     * @param snippet the name of the method whose graph should be processed
134     * @param expectedConstantResult if this is non-null, the resulting graph needs to have the
135     *            given constant return value
136     * @param iterativeEscapeAnalysis true if escape analysis should be run for more than one
137     *            iteration
138     */
139    protected void testEscapeAnalysis(String snippet, JavaConstant expectedConstantResult, boolean iterativeEscapeAnalysis) {
140        prepareGraph(snippet, iterativeEscapeAnalysis);
141        if (expectedConstantResult != null) {
142            for (ReturnNode returnNode : returnNodes) {
143                Assert.assertTrue(returnNode.result().toString(), returnNode.result().isConstant());
144                Assert.assertEquals(expectedConstantResult, returnNode.result().asConstant());
145            }
146        }
147        int newInstanceCount = graph.getNodes().filter(NewInstanceNode.class).count() + graph.getNodes().filter(NewArrayNode.class).count() +
148                        graph.getNodes().filter(CommitAllocationNode.class).count();
149        Assert.assertEquals(0, newInstanceCount);
150    }
151
152    @SuppressWarnings("try")
153    protected void prepareGraph(String snippet, boolean iterativeEscapeAnalysis) {
154        ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
155        DebugContext debug = getDebugContext();
156        try (DebugContext.Scope s = debug.scope(getClass(), method, getCodeCache())) {
157            graph = parseEager(method, AllowAssumptions.YES, debug);
158            context = getDefaultHighTierContext();
159            new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
160            new DeadCodeEliminationPhase().apply(graph);
161            new CanonicalizerPhase().apply(graph, context);
162            new PartialEscapePhase(iterativeEscapeAnalysis, false, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
163            returnNodes = graph.getNodes(ReturnNode.TYPE).snapshot();
164        } catch (Throwable e) {
165            throw debug.handle(e);
166        }
167    }
168}
169