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 }