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