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 */
23package org.graalvm.compiler.core.test;
24
25import java.util.List;
26
27import org.graalvm.compiler.core.common.CompilationIdentifier;
28import org.graalvm.compiler.graph.Node;
29import org.graalvm.compiler.nodes.ConstantNode;
30import org.graalvm.compiler.nodes.Invoke;
31import org.graalvm.compiler.nodes.InvokeNode;
32import org.graalvm.compiler.nodes.StructuredGraph;
33import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
34import org.graalvm.compiler.nodes.ValueNode;
35import org.graalvm.compiler.nodes.debug.OpaqueNode;
36import org.graalvm.compiler.nodes.extended.LoadHubNode;
37import org.graalvm.compiler.nodes.extended.LoadMethodNode;
38import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
39import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
40import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
41import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
42import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
43import org.graalvm.compiler.options.OptionValues;
44import org.junit.Assert;
45import org.junit.BeforeClass;
46import org.junit.Test;
47
48import jdk.vm.ci.meta.JavaKind;
49import jdk.vm.ci.meta.ResolvedJavaMethod;
50
51/**
52 * Tests use of an intrinsic for virtual methods where the call site is indirect. A prime example is
53 * an intrinsic for {@link Object#hashCode()}. The intrinsic can only be used if the call site would
54 * actually dispatch to {@link Object#hashCode()} and not a method that overrides it.
55 */
56public class GuardedIntrinsicTest extends GraalCompilerTest {
57
58    static class Super {
59        int getAge() {
60            return 11;
61        }
62    }
63
64    public static class Person extends Super {
65        int age;
66
67        public Person(int age) {
68            this.age = age;
69        }
70
71        @Override
72        public int getAge() {
73            return age;
74        }
75    }
76
77    @BytecodeParserForceInline
78    public static final Super createSuper() {
79        return new Super();
80    }
81
82    @BytecodeParserNeverInline
83    public static final Super createPerson() {
84        return new Person(42);
85    }
86
87    public static int getSuperAge(Super s) {
88        return s.getAge();
89    }
90
91    public static int getPersonAge(Person p) {
92        return p.getAge();
93    }
94
95    public static int makeSuperAge() {
96        return createSuper().getAge();
97    }
98
99    public static int makePersonAge() {
100        return createPerson().getAge();
101    }
102
103    @BeforeClass
104    public static void init() {
105        // Ensure classes are initialized
106        new Person(0).toString();
107    }
108
109    private StructuredGraph graph;
110    private StructuredGraph parsedForCompile;
111
112    @Override
113    protected StructuredGraph parseForCompile(ResolvedJavaMethod method, CompilationIdentifier compilationId, OptionValues options) {
114        graph = super.parseForCompile(method, compilationId, options);
115        parsedForCompile = (StructuredGraph) graph.copy(graph.getDebug());
116        return graph;
117    }
118
119    @Override
120    protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
121        Registration r = new Registration(invocationPlugins, Super.class);
122
123        r.register1("getAge", Receiver.class, new InvocationPlugin() {
124            @Override
125            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
126                ConstantNode res = b.add(ConstantNode.forInt(new Super().getAge()));
127                b.add(new OpaqueNode(res));
128                b.push(JavaKind.Int, res);
129                return true;
130            }
131        });
132        super.registerInvocationPlugins(invocationPlugins);
133    }
134
135    public static int referenceMakeSuperAge() {
136        return 11;
137    }
138
139    public static int referenceMakePersonAge() {
140        return 42;
141    }
142
143    @Test
144    public void test01() {
145        Super inheritsHC = new Super();
146        Person overridesHC = new Person(0);
147        test("getSuperAge", inheritsHC);
148        test("getSuperAge", overridesHC);
149
150        // Check that the virtual dispatch test exists after bytecode parsing
151        Assert.assertEquals(1, parsedForCompile.getNodes().filter(LoadMethodNode.class).count());
152        Assert.assertEquals(1, parsedForCompile.getNodes().filter(LoadHubNode.class).count());
153
154        // Check for the marker node indicating the intrinsic was applied
155        Assert.assertEquals(1, parsedForCompile.getNodes().filter(OpaqueNode.class).count());
156
157        // Final graph should have a single invoke
158        List<Node> invokes = graph.getNodes().filter(n -> n instanceof Invoke).snapshot();
159        Assert.assertEquals(invokes.toString(), 1, invokes.size());
160    }
161
162    @Test
163    public void test02() {
164        test("getPersonAge", new Person(0));
165
166        // Check that the virtual dispatch test does not exist after bytecode parsing
167        Assert.assertEquals(0, parsedForCompile.getNodes().filter(LoadMethodNode.class).count());
168        Assert.assertEquals(0, parsedForCompile.getNodes().filter(LoadHubNode.class).count());
169
170        Assert.assertEquals(0, parsedForCompile.getNodes().filter(InvokeNode.class).count());
171    }
172
173    @Test
174    public void test03() {
175        test("makeSuperAge");
176
177        // Check that the virtual dispatch test does not exist after bytecode parsing
178        Assert.assertEquals(0, parsedForCompile.getNodes().filter(LoadMethodNode.class).count());
179        Assert.assertEquals(0, parsedForCompile.getNodes().filter(LoadHubNode.class).count());
180
181        StructuredGraph referenceGraph = parseEager("referenceMakeSuperAge", AllowAssumptions.NO);
182        assertEquals(referenceGraph, graph, true, true);
183    }
184
185    @Test
186    public void test04() {
187        test("makePersonAge");
188
189        // Check that the virtual dispatch test exists after bytecode parsing
190        Assert.assertEquals(1, parsedForCompile.getNodes().filter(LoadMethodNode.class).count());
191        Assert.assertEquals(1, parsedForCompile.getNodes().filter(LoadHubNode.class).count());
192
193        StructuredGraph referenceGraph = parseEager("referenceMakePersonAge", AllowAssumptions.NO);
194        assertEquals(referenceGraph, graph, true, true);
195    }
196
197    static final class ReadCacheEntry {
198
199        public final Object identity;
200        public final ValueNode object;
201
202        ReadCacheEntry(Object identity, ValueNode object) {
203            this.identity = identity;
204            this.object = object;
205        }
206
207        @Override
208        public int hashCode() {
209            int result = ((identity == null) ? 0 : identity.hashCode());
210            return result + System.identityHashCode(object);
211        }
212    }
213
214    public static int getHashCode(ReadCacheEntry obj) {
215        return obj.hashCode();
216    }
217
218    @Test
219    public void test05() {
220        ReadCacheEntry val1 = new ReadCacheEntry("identity", ConstantNode.forBoolean(false));
221        ReadCacheEntry val2 = new ReadCacheEntry(Integer.valueOf(34), ConstantNode.forInt(42));
222        for (int i = 0; i < 10000; i++) {
223            getHashCode(val2);
224        }
225        test("getHashCode", val1);
226    }
227}
228