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 }