1 /*
2  * Copyright (c) 2013, 2020, 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.hotspot.stubs;
26 
27 import static org.graalvm.compiler.hotspot.meta.HotSpotForeignCallDescriptor.Reexecutability.REEXECUTABLE;
28 import static org.graalvm.compiler.hotspot.meta.HotSpotForeignCallDescriptor.Transition.SAFEPOINT;
29 import static org.graalvm.compiler.replacements.nodes.CStringConstant.cstring;
30 
31 import java.lang.reflect.Method;
32 import java.lang.reflect.Modifier;
33 import java.util.Arrays;
34 import java.util.List;
35 
36 import org.graalvm.compiler.api.replacements.Fold;
37 import org.graalvm.compiler.api.replacements.Fold.InjectedParameter;
38 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
39 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
40 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
41 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
42 import org.graalvm.compiler.hotspot.meta.HotSpotForeignCallDescriptor;
43 import org.graalvm.compiler.hotspot.nodes.StubForeignCallNode;
44 import org.graalvm.compiler.hotspot.nodes.VMErrorNode;
45 import org.graalvm.compiler.hotspot.replacements.Log;
46 import org.graalvm.compiler.word.Word;
47 import jdk.internal.vm.compiler.word.LocationIdentity;
48 import jdk.internal.vm.compiler.word.WordFactory;
49 
50 //JaCoCo Exclude
51 
52 /**
53  * A collection of methods used in {@link Stub}s.
54  */
55 public class StubUtil {
56 
57     public static final HotSpotForeignCallDescriptor VM_MESSAGE_C = newDescriptor(SAFEPOINT, REEXECUTABLE, null, StubUtil.class, "vmMessageC", void.class, boolean.class, Word.class, long.class,
58                     long.class, long.class);
59 
newDescriptor(HotSpotForeignCallDescriptor.Transition safepoint, HotSpotForeignCallDescriptor.Reexecutability reexecutable, LocationIdentity killLocation, Class<?> stubClass, String name, Class<?> resultType, Class<?>... argumentTypes)60     public static HotSpotForeignCallDescriptor newDescriptor(HotSpotForeignCallDescriptor.Transition safepoint, HotSpotForeignCallDescriptor.Reexecutability reexecutable,
61                     LocationIdentity killLocation,
62                     Class<?> stubClass, String name, Class<?> resultType, Class<?>... argumentTypes) {
63         HotSpotForeignCallDescriptor d = new HotSpotForeignCallDescriptor(safepoint, reexecutable, killLocation, name, resultType, argumentTypes);
64         assert descriptorFor(stubClass, name, resultType, argumentTypes);
65         return d;
66     }
67 
68     /**
69      * Looks for a {@link StubForeignCallNode} node intrinsic named {@code name} in
70      * {@code stubClass} and returns a {@link ForeignCallDescriptor} based on its signature and the
71      * value of {@code hasSideEffect}.
72      */
descriptorFor(Class<?> stubClass, String name, Class<?> resultType, Class<?>[] argumentTypes)73     private static boolean descriptorFor(Class<?> stubClass, String name, Class<?> resultType, Class<?>[] argumentTypes) {
74         Method found = null;
75         for (Method method : stubClass.getDeclaredMethods()) {
76             if (Modifier.isStatic(method.getModifiers()) && method.getAnnotation(NodeIntrinsic.class) != null && method.getName().equals(name)) {
77                 if (method.getAnnotation(NodeIntrinsic.class).value().equals(StubForeignCallNode.class)) {
78                     assert found == null : "found more than one foreign call named " + name + " in " + stubClass;
79                     assert method.getParameterTypes().length != 0 && method.getParameterTypes()[0] == ForeignCallDescriptor.class : "first parameter of foreign call '" + name + "' in " +
80                                     stubClass + " must be of type " + ForeignCallDescriptor.class.getSimpleName();
81                     found = method;
82                 }
83             }
84         }
85         assert found != null : "could not find foreign call named " + name + " in " + stubClass;
86         List<Class<?>> paramList = Arrays.asList(found.getParameterTypes());
87         Class<?>[] cCallTypes = paramList.subList(1, paramList.size()).toArray(new Class<?>[paramList.size() - 1]);
88         assert resultType.equals(found.getReturnType()) : found;
89         assert Arrays.equals(cCallTypes, argumentTypes) : found;
90         return true;
91     }
92 
93     /**
94      * Determines if this is a HotSpot build where the ASSERT mechanism is enabled.
95      */
96     @Fold
cAssertionsEnabled(@njectedParameter GraalHotSpotVMConfig config)97     public static boolean cAssertionsEnabled(@InjectedParameter GraalHotSpotVMConfig config) {
98         return config.cAssertions;
99     }
100 
101     @NodeIntrinsic(StubForeignCallNode.class)
vmMessageC(@onstantNodeParameter ForeignCallDescriptor stubPrintfC, boolean vmError, Word format, long v1, long v2, long v3)102     private static native void vmMessageC(@ConstantNodeParameter ForeignCallDescriptor stubPrintfC, boolean vmError, Word format, long v1, long v2, long v3);
103 
104     /**
105      * Prints a message to the log stream.
106      * <p>
107      * <b>Stubs must use this instead of {@link Log#printf(String, long)} to avoid an object
108      * constant in a RuntimeStub.</b>
109      *
110      * @param message a message string
111      */
printf(String message)112     public static void printf(String message) {
113         vmMessageC(VM_MESSAGE_C, false, cstring(message), 0L, 0L, 0L);
114     }
115 
116     /**
117      * Prints a message to the log stream.
118      * <p>
119      * <b>Stubs must use this instead of {@link Log#printf(String, long)} to avoid an object
120      * constant in a RuntimeStub.</b>
121      *
122      * @param format a C style printf format value
123      * @param value the value associated with the first conversion specifier in {@code format}
124      */
printf(String format, long value)125     public static void printf(String format, long value) {
126         vmMessageC(VM_MESSAGE_C, false, cstring(format), value, 0L, 0L);
127     }
128 
129     /**
130      * Prints a message to the log stream.
131      * <p>
132      * <b>Stubs must use this instead of {@link Log#printf(String, long, long)} to avoid an object
133      * constant in a RuntimeStub.</b>
134      *
135      * @param format a C style printf format value
136      * @param v1 the value associated with the first conversion specifier in {@code format}
137      * @param v2 the value associated with the second conversion specifier in {@code format}
138      */
printf(String format, long v1, long v2)139     public static void printf(String format, long v1, long v2) {
140         vmMessageC(VM_MESSAGE_C, false, cstring(format), v1, v2, 0L);
141     }
142 
143     /**
144      * Prints a message to the log stream.
145      * <p>
146      * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an
147      * object constant in a RuntimeStub.</b>
148      *
149      * @param format a C style printf format value
150      * @param v1 the value associated with the first conversion specifier in {@code format}
151      * @param v2 the value associated with the second conversion specifier in {@code format}
152      * @param v3 the value associated with the third conversion specifier in {@code format}
153      */
printf(String format, long v1, long v2, long v3)154     public static void printf(String format, long v1, long v2, long v3) {
155         vmMessageC(VM_MESSAGE_C, false, cstring(format), v1, v2, v3);
156     }
157 
158     /**
159      * Analyzes a given value and prints information about it to the log stream.
160      */
decipher(long value)161     public static void decipher(long value) {
162         vmMessageC(VM_MESSAGE_C, false, WordFactory.zero(), value, 0L, 0L);
163     }
164 
165     /**
166      * Exits the VM with a given error message.
167      * <p>
168      * <b>Stubs must use this instead of {@link VMErrorNode#vmError(String, long)} to avoid an
169      * object constant in a RuntimeStub.</b>
170      *
171      * @param message an error message
172      */
fatal(String message)173     public static void fatal(String message) {
174         vmMessageC(VM_MESSAGE_C, true, cstring(message), 0L, 0L, 0L);
175     }
176 
177     /**
178      * Exits the VM with a given error message.
179      * <p>
180      * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an
181      * object constant in a RuntimeStub.</b>
182      *
183      * @param format a C style printf format value
184      * @param value the value associated with the first conversion specifier in {@code format}
185      */
fatal(String format, long value)186     public static void fatal(String format, long value) {
187         vmMessageC(VM_MESSAGE_C, true, cstring(format), value, 0L, 0L);
188     }
189 
190     /**
191      * Exits the VM with a given error message.
192      * <p>
193      * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an
194      * object constant in a RuntimeStub.</b>
195      *
196      * @param format a C style printf format value
197      * @param v1 the value associated with the first conversion specifier in {@code format}
198      * @param v2 the value associated with the second conversion specifier in {@code format}
199      */
fatal(String format, long v1, long v2)200     public static void fatal(String format, long v1, long v2) {
201         vmMessageC(VM_MESSAGE_C, true, cstring(format), v1, v2, 0L);
202     }
203 
204     /**
205      * Exits the VM with a given error message.
206      * <p>
207      * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an
208      * object constant in a RuntimeStub.</b>
209      *
210      * @param format a C style printf format value
211      * @param v1 the value associated with the first conversion specifier in {@code format}
212      * @param v2 the value associated with the second conversion specifier in {@code format}
213      * @param v3 the value associated with the third conversion specifier in {@code format}
214      */
fatal(String format, long v1, long v2, long v3)215     public static void fatal(String format, long v1, long v2, long v3) {
216         vmMessageC(VM_MESSAGE_C, true, cstring(format), v1, v2, v3);
217     }
218 
219     /**
220      * Print {@code number} as decimal string to {@code buffer}.
221      *
222      * @param buffer
223      * @param number
224      * @return A pointer pointing one byte right after the last printed digit in {@code buffer}.
225      */
printNumber(Word buffer, long number)226     public static Word printNumber(Word buffer, long number) {
227         long tmpNumber = number;
228         int offset;
229         if (tmpNumber <= 0) {
230             tmpNumber = -tmpNumber;
231             offset = 1;
232         } else {
233             offset = 0;
234         }
235         while (tmpNumber > 0) {
236             tmpNumber /= 10;
237             offset++;
238         }
239         tmpNumber = number < 0 ? -number : number;
240         Word ptr = buffer.add(offset);
241         do {
242             long digit = tmpNumber % 10;
243             tmpNumber /= 10;
244             ptr = ptr.subtract(1);
245             ptr.writeByte(0, (byte) ('0' + digit));
246         } while (tmpNumber > 0);
247 
248         if (number < 0) {
249             ptr = ptr.subtract(1);
250             ptr.writeByte(0, (byte) '-');
251         }
252         return buffer.add(offset);
253     }
254 
255     /**
256      * Copy {@code javaString} bytes to the memory location {@code ptr}.
257      *
258      * @param buffer
259      * @param javaString
260      * @return A pointer pointing one byte right after the last byte copied from {@code javaString}
261      *         to {@code ptr}
262      */
printString(Word buffer, String javaString)263     public static Word printString(Word buffer, String javaString) {
264         Word string = cstring(javaString);
265         int i = 0;
266         byte b;
267         while ((b = string.readByte(i)) != 0) {
268             buffer.writeByte(i, b);
269             i++;
270         }
271         return buffer.add(i);
272     }
273 }
274