1 /* 2 * Copyright (c) 2013, 2018, 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 jdk.vm.ci.meta.DeoptimizationReason.RuntimeConstraint; 28 import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfigBase.INJECTED_VMCONFIG; 29 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.clearPendingException; 30 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.getAndClearObjectResult; 31 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.loadHubIntrinsic; 32 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.verifyOops; 33 import static org.graalvm.compiler.replacements.nodes.CStringConstant.cstring; 34 35 import java.lang.reflect.Method; 36 import java.lang.reflect.Modifier; 37 import java.util.Arrays; 38 import java.util.List; 39 40 import org.graalvm.compiler.api.replacements.Fold; 41 import org.graalvm.compiler.api.replacements.Fold.InjectedParameter; 42 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; 43 import org.graalvm.compiler.graph.Node.ConstantNodeParameter; 44 import org.graalvm.compiler.graph.Node.NodeIntrinsic; 45 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 46 import org.graalvm.compiler.hotspot.nodes.DeoptimizeCallerNode; 47 import org.graalvm.compiler.hotspot.nodes.StubForeignCallNode; 48 import org.graalvm.compiler.hotspot.nodes.VMErrorNode; 49 import org.graalvm.compiler.hotspot.word.KlassPointer; 50 import org.graalvm.compiler.nodes.PiNode; 51 import org.graalvm.compiler.nodes.SnippetAnchorNode; 52 import org.graalvm.compiler.nodes.extended.GuardingNode; 53 import org.graalvm.compiler.replacements.Log; 54 import org.graalvm.compiler.word.Word; 55 import jdk.internal.vm.compiler.word.Pointer; 56 import jdk.internal.vm.compiler.word.WordFactory; 57 58 import jdk.vm.ci.meta.DeoptimizationAction; 59 60 //JaCoCo Exclude 61 62 /** 63 * A collection of methods used in {@link Stub}s. 64 */ 65 public class StubUtil { 66 67 public static final ForeignCallDescriptor VM_MESSAGE_C = newDescriptor(StubUtil.class, "vmMessageC", void.class, boolean.class, Word.class, long.class, long.class, long.class); 68 newDescriptor(Class<?> stubClass, String name, Class<?> resultType, Class<?>... argumentTypes)69 public static ForeignCallDescriptor newDescriptor(Class<?> stubClass, String name, Class<?> resultType, Class<?>... argumentTypes) { 70 ForeignCallDescriptor d = new ForeignCallDescriptor(name, resultType, argumentTypes); 71 assert descriptorFor(stubClass, name).equals(d) : descriptorFor(stubClass, name) + " != " + d; 72 return d; 73 } 74 75 /** 76 * Looks for a {@link StubForeignCallNode} node intrinsic named {@code name} in 77 * {@code stubClass} and returns a {@link ForeignCallDescriptor} based on its signature and the 78 * value of {@code hasSideEffect}. 79 */ descriptorFor(Class<?> stubClass, String name)80 private static ForeignCallDescriptor descriptorFor(Class<?> stubClass, String name) { 81 Method found = null; 82 for (Method method : stubClass.getDeclaredMethods()) { 83 if (Modifier.isStatic(method.getModifiers()) && method.getAnnotation(NodeIntrinsic.class) != null && method.getName().equals(name)) { 84 if (method.getAnnotation(NodeIntrinsic.class).value().equals(StubForeignCallNode.class)) { 85 assert found == null : "found more than one foreign call named " + name + " in " + stubClass; 86 assert method.getParameterTypes().length != 0 && method.getParameterTypes()[0] == ForeignCallDescriptor.class : "first parameter of foreign call '" + name + "' in " + stubClass + 87 " must be of type " + ForeignCallDescriptor.class.getSimpleName(); 88 found = method; 89 } 90 } 91 } 92 assert found != null : "could not find foreign call named " + name + " in " + stubClass; 93 List<Class<?>> paramList = Arrays.asList(found.getParameterTypes()); 94 Class<?>[] cCallTypes = paramList.subList(1, paramList.size()).toArray(new Class<?>[paramList.size() - 1]); 95 return new ForeignCallDescriptor(name, found.getReturnType(), cCallTypes); 96 } 97 handlePendingException(Word thread, boolean isObjectResult)98 public static void handlePendingException(Word thread, boolean isObjectResult) { 99 if (clearPendingException(thread) != null) { 100 if (isObjectResult) { 101 getAndClearObjectResult(thread); 102 } 103 DeoptimizeCallerNode.deopt(DeoptimizationAction.None, RuntimeConstraint); 104 } 105 } 106 107 /** 108 * Determines if this is a HotSpot build where the ASSERT mechanism is enabled. 109 */ 110 @Fold cAssertionsEnabled(@njectedParameter GraalHotSpotVMConfig config)111 public static boolean cAssertionsEnabled(@InjectedParameter GraalHotSpotVMConfig config) { 112 return config.cAssertions; 113 } 114 115 @NodeIntrinsic(StubForeignCallNode.class) vmMessageC(@onstantNodeParameter ForeignCallDescriptor stubPrintfC, boolean vmError, Word format, long v1, long v2, long v3)116 private static native void vmMessageC(@ConstantNodeParameter ForeignCallDescriptor stubPrintfC, boolean vmError, Word format, long v1, long v2, long v3); 117 118 /** 119 * Prints a message to the log stream. 120 * <p> 121 * <b>Stubs must use this instead of {@link Log#printf(String, long)} to avoid an object 122 * constant in a RuntimeStub.</b> 123 * 124 * @param message a message string 125 */ printf(String message)126 public static void printf(String message) { 127 vmMessageC(VM_MESSAGE_C, false, cstring(message), 0L, 0L, 0L); 128 } 129 130 /** 131 * Prints a message to the log stream. 132 * <p> 133 * <b>Stubs must use this instead of {@link Log#printf(String, long)} to avoid an object 134 * constant in a RuntimeStub.</b> 135 * 136 * @param format a C style printf format value 137 * @param value the value associated with the first conversion specifier in {@code format} 138 */ printf(String format, long value)139 public static void printf(String format, long value) { 140 vmMessageC(VM_MESSAGE_C, false, cstring(format), value, 0L, 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)} to avoid an object 147 * 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 */ printf(String format, long v1, long v2)153 public static void printf(String format, long v1, long v2) { 154 vmMessageC(VM_MESSAGE_C, false, cstring(format), v1, v2, 0L); 155 } 156 157 /** 158 * Prints a message to the log stream. 159 * <p> 160 * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an 161 * object constant in a RuntimeStub.</b> 162 * 163 * @param format a C style printf format value 164 * @param v1 the value associated with the first conversion specifier in {@code format} 165 * @param v2 the value associated with the second conversion specifier in {@code format} 166 * @param v3 the value associated with the third conversion specifier in {@code format} 167 */ printf(String format, long v1, long v2, long v3)168 public static void printf(String format, long v1, long v2, long v3) { 169 vmMessageC(VM_MESSAGE_C, false, cstring(format), v1, v2, v3); 170 } 171 172 /** 173 * Analyzes a given value and prints information about it to the log stream. 174 */ decipher(long value)175 public static void decipher(long value) { 176 vmMessageC(VM_MESSAGE_C, false, WordFactory.zero(), value, 0L, 0L); 177 } 178 179 /** 180 * Exits the VM with a given error message. 181 * <p> 182 * <b>Stubs must use this instead of {@link VMErrorNode#vmError(String, long)} to avoid an 183 * object constant in a RuntimeStub.</b> 184 * 185 * @param message an error message 186 */ fatal(String message)187 public static void fatal(String message) { 188 vmMessageC(VM_MESSAGE_C, true, cstring(message), 0L, 0L, 0L); 189 } 190 191 /** 192 * Exits the VM with a given error message. 193 * <p> 194 * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an 195 * object constant in a RuntimeStub.</b> 196 * 197 * @param format a C style printf format value 198 * @param value the value associated with the first conversion specifier in {@code format} 199 */ fatal(String format, long value)200 public static void fatal(String format, long value) { 201 vmMessageC(VM_MESSAGE_C, true, cstring(format), value, 0L, 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 */ fatal(String format, long v1, long v2)214 public static void fatal(String format, long v1, long v2) { 215 vmMessageC(VM_MESSAGE_C, true, cstring(format), v1, v2, 0L); 216 } 217 218 /** 219 * Exits the VM with a given error message. 220 * <p> 221 * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an 222 * object constant in a RuntimeStub.</b> 223 * 224 * @param format a C style printf format value 225 * @param v1 the value associated with the first conversion specifier in {@code format} 226 * @param v2 the value associated with the second conversion specifier in {@code format} 227 * @param v3 the value associated with the third conversion specifier in {@code format} 228 */ fatal(String format, long v1, long v2, long v3)229 public static void fatal(String format, long v1, long v2, long v3) { 230 vmMessageC(VM_MESSAGE_C, true, cstring(format), v1, v2, v3); 231 } 232 233 /** 234 * Verifies that a given object value is well formed if {@code -XX:+VerifyOops} is enabled. 235 */ verifyObject(Object object)236 public static Object verifyObject(Object object) { 237 if (verifyOops(INJECTED_VMCONFIG)) { 238 Word verifyOopCounter = WordFactory.unsigned(verifyOopCounterAddress(INJECTED_VMCONFIG)); 239 verifyOopCounter.writeInt(0, verifyOopCounter.readInt(0) + 1); 240 241 Pointer oop = Word.objectToTrackedPointer(object); 242 if (object != null) { 243 GuardingNode anchorNode = SnippetAnchorNode.anchor(); 244 // make sure object is 'reasonable' 245 if (!oop.and(WordFactory.unsigned(verifyOopMask(INJECTED_VMCONFIG))).equal(WordFactory.unsigned(verifyOopBits(INJECTED_VMCONFIG)))) { 246 fatal("oop not in heap: %p", oop.rawValue()); 247 } 248 249 KlassPointer klass = loadHubIntrinsic(PiNode.piCastNonNull(object, anchorNode)); 250 if (klass.isNull()) { 251 fatal("klass for oop %p is null", oop.rawValue()); 252 } 253 } 254 } 255 return object; 256 } 257 258 @Fold verifyOopCounterAddress(@njectedParameter GraalHotSpotVMConfig config)259 static long verifyOopCounterAddress(@InjectedParameter GraalHotSpotVMConfig config) { 260 return config.verifyOopCounterAddress; 261 } 262 263 @Fold verifyOopMask(@njectedParameter GraalHotSpotVMConfig config)264 static long verifyOopMask(@InjectedParameter GraalHotSpotVMConfig config) { 265 return config.verifyOopMask; 266 } 267 268 @Fold verifyOopBits(@njectedParameter GraalHotSpotVMConfig config)269 static long verifyOopBits(@InjectedParameter GraalHotSpotVMConfig config) { 270 return config.verifyOopBits; 271 } 272 273 @Fold hubOffset(@njectedParameter GraalHotSpotVMConfig config)274 static int hubOffset(@InjectedParameter GraalHotSpotVMConfig config) { 275 return config.hubOffset; 276 } 277 278 /** 279 * Print {@code number} as decimal string to {@code buffer}. 280 * 281 * @param buffer 282 * @param number 283 * @return A pointer pointing one byte right after the last printed digit in {@code buffer}. 284 */ printNumber(Word buffer, long number)285 public static Word printNumber(Word buffer, long number) { 286 long tmpNumber = number; 287 int offset; 288 if (tmpNumber <= 0) { 289 tmpNumber = -tmpNumber; 290 offset = 1; 291 } else { 292 offset = 0; 293 } 294 while (tmpNumber > 0) { 295 tmpNumber /= 10; 296 offset++; 297 } 298 tmpNumber = number < 0 ? -number : number; 299 Word ptr = buffer.add(offset); 300 do { 301 long digit = tmpNumber % 10; 302 tmpNumber /= 10; 303 ptr = ptr.subtract(1); 304 ptr.writeByte(0, (byte) ('0' + digit)); 305 } while (tmpNumber > 0); 306 307 if (number < 0) { 308 ptr = ptr.subtract(1); 309 ptr.writeByte(0, (byte) '-'); 310 } 311 return buffer.add(offset); 312 } 313 314 /** 315 * Copy {@code javaString} bytes to the memory location {@code ptr}. 316 * 317 * @param buffer 318 * @param javaString 319 * @return A pointer pointing one byte right after the last byte copied from {@code javaString} 320 * to {@code ptr} 321 */ printString(Word buffer, String javaString)322 public static Word printString(Word buffer, String javaString) { 323 Word string = cstring(javaString); 324 int i = 0; 325 byte b; 326 while ((b = string.readByte(i)) != 0) { 327 buffer.writeByte(i, b); 328 i++; 329 } 330 return buffer.add(i); 331 } 332 } 333