1 /*
2  * Copyright (c) 2015, 2019, 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 package org.graalvm.compiler.replacements.test;
26 
27 import java.util.Objects;
28 
29 import org.graalvm.compiler.api.directives.GraalDirectives;
30 import org.graalvm.compiler.debug.DebugContext;
31 import org.graalvm.compiler.debug.DebugContext.Scope;
32 import org.graalvm.compiler.debug.GraalError;
33 import org.graalvm.compiler.nodes.StructuredGraph;
34 import org.graalvm.compiler.nodes.ValueNode;
35 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
36 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
37 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
38 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
39 import org.graalvm.compiler.replacements.Snippets;
40 import org.graalvm.compiler.replacements.classfile.ClassfileBytecodeProvider;
41 import org.graalvm.compiler.word.Word;
42 import org.graalvm.compiler.word.WordCastNode;
43 import org.junit.Assert;
44 import org.junit.Rule;
45 import org.junit.Test;
46 import org.junit.rules.ExpectedException;
47 
48 import jdk.vm.ci.meta.ResolvedJavaMethod;
49 
50 /**
51  * Tests for derived oops in reference maps.
52  */
53 public class DerivedOopTest extends ReplacementsTest implements Snippets {
54 
55     private static class Pointers {
56         public long basePointer;
57         public long internalPointer;
58 
delta()59         public long delta() {
60             return internalPointer - basePointer;
61         }
62 
63         @Override
equals(Object obj)64         public boolean equals(Object obj) {
65             if (!(obj instanceof Pointers)) {
66                 return false;
67             }
68 
69             Pointers other = (Pointers) obj;
70             return this.delta() == other.delta();
71         }
72 
73         @Override
hashCode()74         public int hashCode() {
75             return (int) delta();
76         }
77     }
78 
79     private static class Result {
80         public Pointers beforeGC;
81         public Pointers afterGC;
82 
Result()83         Result() {
84             beforeGC = new Pointers();
85             afterGC = new Pointers();
86         }
87 
88         @Override
hashCode()89         public int hashCode() {
90             final int prime = 31;
91             int result = 1;
92             result = prime * result + ((afterGC == null) ? 0 : afterGC.hashCode());
93             result = prime * result + ((beforeGC == null) ? 0 : beforeGC.hashCode());
94             return result;
95         }
96 
97         @Override
equals(Object obj)98         public boolean equals(Object obj) {
99             if (!(obj instanceof Result)) {
100                 return false;
101             }
102             Result other = (Result) obj;
103             return Objects.equals(this.beforeGC, other.beforeGC) && Objects.equals(this.afterGC, other.afterGC);
104         }
105     }
106 
107     @Test
testFieldOffset()108     public void testFieldOffset() {
109         // Run a couple times to encourage objects to move
110         for (int i = 0; i < 4; i++) {
111             Result r = new Result();
112             test("fieldOffsetSnippet", r, 16L);
113 
114             Assert.assertEquals(r.beforeGC.delta(), r.afterGC.delta());
115         }
116     }
117 
getRawPointer(Object obj)118     static long getRawPointer(Object obj) {
119         // fake implementation for interpreter
120         return obj.hashCode();
121     }
122 
getRawPointerIntrinsic(Object obj)123     static long getRawPointerIntrinsic(Object obj) {
124         return Word.objectToTrackedPointer(obj).rawValue();
125     }
126 
fieldOffsetSnippet(Result obj, long offset)127     public static Result fieldOffsetSnippet(Result obj, long offset) {
128         long internalPointer = getRawPointer(obj) + offset;
129 
130         // make sure the internal pointer is computed before the safepoint
131         GraalDirectives.blackhole(internalPointer);
132 
133         obj.beforeGC.basePointer = getRawPointer(obj);
134         obj.beforeGC.internalPointer = internalPointer;
135 
136         System.gc();
137 
138         obj.afterGC.basePointer = getRawPointer(obj);
139         obj.afterGC.internalPointer = internalPointer;
140 
141         return obj;
142     }
143 
144     @Rule public final ExpectedException thrown = ExpectedException.none();
145     private static final String UNKNOWN_REFERENCE_AT_SAFEPOINT_MSG = "should not reach here: unknown reference alive across safepoint";
146 
147     @Test
148     @SuppressWarnings("try")
testFieldOffsetMergeNonLiveBasePointer()149     public void testFieldOffsetMergeNonLiveBasePointer() {
150         thrown.expect(GraalError.class);
151         thrown.expectMessage(UNKNOWN_REFERENCE_AT_SAFEPOINT_MSG);
152         DebugContext debug = getDebugContext();
153         try (Scope s = debug.disable()) {
154             // Run a couple times to encourage objects to move
155             for (int i = 0; i < 4; i++) {
156                 Result r = new Result();
157                 test("fieldOffsetMergeSnippet01", r, 8L, 16L);
158                 Assert.assertEquals(r.beforeGC.delta(), r.afterGC.delta());
159             }
160         }
161     }
162 
163     @Test
testFieldOffsetMergeNonLiveBasePointerNotAccrossSafepoint()164     public void testFieldOffsetMergeNonLiveBasePointerNotAccrossSafepoint() {
165         // Run a couple times to encourage objects to move
166         for (int i = 0; i < 4; i++) {
167             Result r = new Result();
168             test("fieldOffsetMergeSnippet02", r, 8L, 16L);
169         }
170     }
171 
172     @Test
173     @SuppressWarnings("try")
testFieldOffsetMergeLiveBasePointer()174     public void testFieldOffsetMergeLiveBasePointer() {
175         thrown.expect(GraalError.class);
176         thrown.expectMessage(UNKNOWN_REFERENCE_AT_SAFEPOINT_MSG);
177         DebugContext debug = getDebugContext();
178         try (Scope s = debug.disable()) {
179             // Run a couple times to encourage objects to move
180             for (int i = 0; i < 4; i++) {
181                 Result r = new Result();
182                 test("fieldOffsetMergeSnippet03", r, new Result(), new Result(), 8L, 16L);
183                 Assert.assertEquals(r.beforeGC.delta(), r.afterGC.delta());
184             }
185         }
186     }
187 
188     public static boolean SideEffectB;
189     public static long SideEffect1 = 16;
190     public static long SideEffect2 = 16;
191     public static Object o1 = new Result();
192     public static Object o2 = o1;
193 
fieldOffsetMergeSnippet01(Result objResult, long offsetA, long offsetB)194     public static Result fieldOffsetMergeSnippet01(Result objResult, long offsetA, long offsetB) {
195         long internalPointer;
196         if (SideEffectB) {
197             internalPointer = getRawPointer(o1) + offsetA;
198             SideEffect1 = internalPointer;
199         } else {
200             internalPointer = getRawPointer(o2) + offsetB;
201             SideEffect2 = internalPointer;
202         }
203         GraalDirectives.controlFlowAnchor();
204         // make sure the internal pointer is computed before the safepoint
205         GraalDirectives.blackhole(internalPointer);
206         objResult.beforeGC.basePointer = getRawPointer(objResult);
207         objResult.beforeGC.internalPointer = internalPointer;
208         System.gc();
209         objResult.afterGC.basePointer = getRawPointer(objResult);
210         objResult.afterGC.internalPointer = internalPointer;
211         return objResult;
212     }
213 
fieldOffsetMergeSnippet02(Result objResult, long offsetA, long offsetB)214     public static Result fieldOffsetMergeSnippet02(Result objResult, long offsetA, long offsetB) {
215         long internalPointer;
216         if (SideEffectB) {
217             internalPointer = getRawPointer(o1) + offsetA;
218             SideEffect1 = internalPointer;
219         } else {
220             internalPointer = getRawPointer(o2) + offsetB;
221             SideEffect2 = internalPointer;
222         }
223         GraalDirectives.controlFlowAnchor();
224         // make sure the internal pointer is computed before the safepoint
225         GraalDirectives.blackhole(internalPointer);
226         objResult.beforeGC.basePointer = getRawPointer(objResult);
227         objResult.beforeGC.internalPointer = internalPointer;
228         objResult.afterGC.basePointer = getRawPointer(objResult);
229         objResult.afterGC.internalPointer = internalPointer;
230         return objResult;
231     }
232 
fieldOffsetMergeSnippet03(Result objResult, Result a, Result b, long offsetA, long offsetB)233     public static Result fieldOffsetMergeSnippet03(Result objResult, Result a, Result b, long offsetA, long offsetB) {
234         long internalPointer;
235         if (SideEffectB) {
236             internalPointer = getRawPointer(a) + offsetA;
237             SideEffect1 = internalPointer;
238         } else {
239             internalPointer = getRawPointer(b) + offsetB;
240             SideEffect2 = internalPointer;
241         }
242         GraalDirectives.controlFlowAnchor();
243         // make sure the internal pointer is computed before the safepoint
244         GraalDirectives.blackhole(internalPointer);
245         objResult.beforeGC.basePointer = getRawPointer(objResult);
246         objResult.beforeGC.internalPointer = internalPointer;
247         System.gc();
248         objResult.afterGC.basePointer = getRawPointer(objResult);
249         objResult.afterGC.internalPointer = internalPointer;
250         return objResult;
251     }
252 
253     @Override
registerInvocationPlugins(InvocationPlugins invocationPlugins)254     protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
255         Registration r = new Registration(invocationPlugins, DerivedOopTest.class);
256         ClassfileBytecodeProvider bytecodeProvider = getSystemClassLoaderBytecodeProvider();
257 
258         ResolvedJavaMethod intrinsic = getResolvedJavaMethod("getRawPointerIntrinsic");
259         r.register1("getRawPointer", Object.class, new InvocationPlugin() {
260             @Override
261             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
262                 return b.intrinsify(bytecodeProvider, targetMethod, intrinsic, receiver, new ValueNode[]{arg});
263             }
264         });
265         super.registerInvocationPlugins(invocationPlugins);
266     }
267 
268     @Override
checkHighTierGraph(StructuredGraph graph)269     protected void checkHighTierGraph(StructuredGraph graph) {
270         assert graph.getNodes().filter(WordCastNode.class).count() > 0 : "DerivedOopTest.toLong should be intrinsified";
271         super.checkHighTierGraph(graph);
272     }
273 }
274