1 /* 2 * Copyright (c) 2013, 2016, 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 package org.graalvm.compiler.hotspot.stubs; 24 25 import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_VMCONFIG; 26 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.clearPendingException; 27 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.getAndClearObjectResult; 28 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.loadHubIntrinsic; 29 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.verifyOops; 30 import static org.graalvm.compiler.replacements.nodes.CStringConstant.cstring; 31 import static org.graalvm.compiler.word.Word.unsigned; 32 import static jdk.vm.ci.meta.DeoptimizationReason.RuntimeConstraint; 33 34 import java.lang.reflect.Method; 35 import java.lang.reflect.Modifier; 36 import java.util.Arrays; 37 import java.util.List; 38 39 import org.graalvm.compiler.api.replacements.Fold; 40 import org.graalvm.compiler.api.replacements.Fold.InjectedParameter; 41 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; 42 import org.graalvm.compiler.graph.Node.ConstantNodeParameter; 43 import org.graalvm.compiler.graph.Node.NodeIntrinsic; 44 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 45 import org.graalvm.compiler.hotspot.nodes.DeoptimizeCallerNode; 46 import org.graalvm.compiler.hotspot.nodes.SnippetAnchorNode; 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.extended.GuardingNode; 52 import org.graalvm.compiler.replacements.Log; 53 import org.graalvm.compiler.word.Pointer; 54 import org.graalvm.compiler.word.Word; 55 56 import jdk.vm.ci.meta.DeoptimizationAction; 57 58 //JaCoCo Exclude 59 60 /** 61 * A collection of methods used in {@link Stub}s. 62 */ 63 public class StubUtil { 64 65 public static final ForeignCallDescriptor VM_MESSAGE_C = newDescriptor(StubUtil.class, "vmMessageC", void.class, boolean.class, Word.class, long.class, long.class, long.class); 66 67 public static ForeignCallDescriptor newDescriptor(Class<?> stubClass, String name, Class<?> resultType, Class<?>... argumentTypes) { 68 ForeignCallDescriptor d = new ForeignCallDescriptor(name, resultType, argumentTypes); 69 assert descriptorFor(stubClass, name).equals(d) : descriptorFor(stubClass, name) + " != " + d; 70 return d; 71 } 72 73 /** 74 * Looks for a {@link StubForeignCallNode} node intrinsic named {@code name} in 75 * {@code stubClass} and returns a {@link ForeignCallDescriptor} based on its signature and the 76 * value of {@code hasSideEffect}. 77 */ 78 private static ForeignCallDescriptor descriptorFor(Class<?> stubClass, String name) { 79 Method found = null; 80 for (Method method : stubClass.getDeclaredMethods()) { 81 if (Modifier.isStatic(method.getModifiers()) && method.getAnnotation(NodeIntrinsic.class) != null && method.getName().equals(name)) { 82 if (method.getAnnotation(NodeIntrinsic.class).value().equals(StubForeignCallNode.class)) { 83 assert found == null : "found more than one foreign call named " + name + " in " + stubClass; 84 assert method.getParameterTypes().length != 0 && method.getParameterTypes()[0] == ForeignCallDescriptor.class : "first parameter of foreign call '" + name + "' in " + stubClass + 85 " must be of type " + ForeignCallDescriptor.class.getSimpleName(); 86 found = method; 87 } 88 } 89 } 90 assert found != null : "could not find foreign call named " + name + " in " + stubClass; 91 List<Class<?>> paramList = Arrays.asList(found.getParameterTypes()); 92 Class<?>[] cCallTypes = paramList.subList(1, paramList.size()).toArray(new Class<?>[paramList.size() - 1]); 93 return new ForeignCallDescriptor(name, found.getReturnType(), cCallTypes); 94 } 95 96 public static void handlePendingException(Word thread, boolean isObjectResult) { 97 if (clearPendingException(thread) != null) { 98 if (isObjectResult) { 99 getAndClearObjectResult(thread); 100 } 101 DeoptimizeCallerNode.deopt(DeoptimizationAction.None, RuntimeConstraint); 102 } 103 } 104 105 /** 106 * Determines if this is a HotSpot build where the ASSERT mechanism is enabled. 107 */ 108 @Fold 109 public static boolean cAssertionsEnabled(@InjectedParameter GraalHotSpotVMConfig config) { 110 return config.cAssertions; 111 } 112 113 @NodeIntrinsic(StubForeignCallNode.class) 114 private static native void vmMessageC(@ConstantNodeParameter ForeignCallDescriptor stubPrintfC, boolean vmError, Word format, long v1, long v2, long v3); 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 message a message string 123 */ 124 public static void printf(String message) { 125 vmMessageC(VM_MESSAGE_C, false, cstring(message), 0L, 0L, 0L); 126 } 127 128 /** 129 * Prints a message to the log stream. 130 * <p> 131 * <b>Stubs must use this instead of {@link Log#printf(String, long)} to avoid an object 132 * constant in a RuntimeStub.</b> 133 * 134 * @param format a C style printf format value 135 * @param value the value associated with the first conversion specifier in {@code format} 136 */ 137 public static void printf(String format, long value) { 138 vmMessageC(VM_MESSAGE_C, false, cstring(format), value, 0L, 0L); 139 } 140 141 /** 142 * Prints a message to the log stream. 143 * <p> 144 * <b>Stubs must use this instead of {@link Log#printf(String, long, long)} to avoid an object 145 * constant in a RuntimeStub.</b> 146 * 147 * @param format a C style printf format value 148 * @param v1 the value associated with the first conversion specifier in {@code format} 149 * @param v2 the value associated with the second conversion specifier in {@code format} 150 */ 151 public static void printf(String format, long v1, long v2) { 152 vmMessageC(VM_MESSAGE_C, false, cstring(format), v1, v2, 0L); 153 } 154 155 /** 156 * Prints a message to the log stream. 157 * <p> 158 * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an 159 * object constant in a RuntimeStub.</b> 160 * 161 * @param format a C style printf format value 162 * @param v1 the value associated with the first conversion specifier in {@code format} 163 * @param v2 the value associated with the second conversion specifier in {@code format} 164 * @param v3 the value associated with the third conversion specifier in {@code format} 165 */ 166 public static void printf(String format, long v1, long v2, long v3) { 167 vmMessageC(VM_MESSAGE_C, false, cstring(format), v1, v2, v3); 168 } 169 170 /** 171 * Analyzes a given value and prints information about it to the log stream. 172 */ 173 public static void decipher(long value) { 174 vmMessageC(VM_MESSAGE_C, false, Word.zero(), value, 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 VMErrorNode#vmError(String, long)} to avoid an 181 * object constant in a RuntimeStub.</b> 182 * 183 * @param message an error message 184 */ 185 public static void fatal(String message) { 186 vmMessageC(VM_MESSAGE_C, true, cstring(message), 0L, 0L, 0L); 187 } 188 189 /** 190 * Exits the VM with a given error message. 191 * <p> 192 * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an 193 * object constant in a RuntimeStub.</b> 194 * 195 * @param format a C style printf format value 196 * @param value the value associated with the first conversion specifier in {@code format} 197 */ 198 public static void fatal(String format, long value) { 199 vmMessageC(VM_MESSAGE_C, true, cstring(format), value, 0L, 0L); 200 } 201 202 /** 203 * Exits the VM with a given error message. 204 * <p> 205 * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an 206 * object constant in a RuntimeStub.</b> 207 * 208 * @param format a C style printf format value 209 * @param v1 the value associated with the first conversion specifier in {@code format} 210 * @param v2 the value associated with the second conversion specifier in {@code format} 211 */ 212 public static void fatal(String format, long v1, long v2) { 213 vmMessageC(VM_MESSAGE_C, true, cstring(format), v1, v2, 0L); 214 } 215 216 /** 217 * Exits the VM with a given error message. 218 * <p> 219 * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an 220 * object constant in a RuntimeStub.</b> 221 * 222 * @param format a C style printf format value 223 * @param v1 the value associated with the first conversion specifier in {@code format} 224 * @param v2 the value associated with the second conversion specifier in {@code format} 225 * @param v3 the value associated with the third conversion specifier in {@code format} 226 */ 227 public static void fatal(String format, long v1, long v2, long v3) { 228 vmMessageC(VM_MESSAGE_C, true, cstring(format), v1, v2, v3); 229 } 230 231 /** 232 * Verifies that a given object value is well formed if {@code -XX:+VerifyOops} is enabled. 233 */ 234 public static Object verifyObject(Object object) { 235 if (verifyOops(INJECTED_VMCONFIG)) { 236 Word verifyOopCounter = Word.unsigned(verifyOopCounterAddress(INJECTED_VMCONFIG)); 237 verifyOopCounter.writeInt(0, verifyOopCounter.readInt(0) + 1); 238 239 Pointer oop = Word.objectToTrackedPointer(object); 240 if (object != null) { 241 GuardingNode anchorNode = SnippetAnchorNode.anchor(); 242 // make sure object is 'reasonable' 243 if (!oop.and(unsigned(verifyOopMask(INJECTED_VMCONFIG))).equal(unsigned(verifyOopBits(INJECTED_VMCONFIG)))) { 244 fatal("oop not in heap: %p", oop.rawValue()); 245 } 246 247 KlassPointer klass = loadHubIntrinsic(PiNode.piCastNonNull(object, anchorNode)); 248 if (klass.isNull()) { 249 fatal("klass for oop %p is null", oop.rawValue()); 250 } 251 } 252 } 253 return object; 254 } 255 256 @Fold 257 static long verifyOopCounterAddress(@InjectedParameter GraalHotSpotVMConfig config) { 258 return config.verifyOopCounterAddress; 259 } 260 261 @Fold 262 static long verifyOopMask(@InjectedParameter GraalHotSpotVMConfig config) { 263 return config.verifyOopMask; 264 } 265 266 @Fold 267 static long verifyOopBits(@InjectedParameter GraalHotSpotVMConfig config) { 268 return config.verifyOopBits; 269 } 270 271 @Fold 272 static int hubOffset(@InjectedParameter GraalHotSpotVMConfig config) { 273 return config.hubOffset; 274 } 275 }