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