1 /*
   2  * Copyright (c) 2015, 2020, 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 jdk.vm.ci.hotspot;
  24 
  25 import static jdk.vm.ci.common.InitTimer.timer;
  26 import static jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory.CompilationLevelAdjustment.None;
  27 import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE;
  28 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
  29 
  30 import java.io.IOException;
  31 import java.io.OutputStream;
  32 import java.io.PrintStream;
  33 import java.io.Serializable;
  34 import java.lang.invoke.CallSite;
  35 import java.lang.invoke.ConstantCallSite;
  36 import java.lang.invoke.MethodHandle;
  37 import java.lang.ref.WeakReference;
  38 import java.util.ArrayList;
  39 import java.util.Collections;
  40 import java.util.Formatter;
  41 import java.util.HashMap;
  42 import java.util.List;
  43 import java.util.Map;
  44 import java.util.Objects;
  45 import java.util.ServiceLoader;
  46 import java.util.function.Predicate;
  47 
  48 import jdk.vm.ci.code.Architecture;
  49 import jdk.vm.ci.code.CompilationRequestResult;
  50 import jdk.vm.ci.code.CompiledCode;
  51 import jdk.vm.ci.code.InstalledCode;
  52 import jdk.vm.ci.common.InitTimer;
  53 import jdk.vm.ci.common.JVMCIError;
  54 import jdk.vm.ci.common.NativeImageReinitialize;
  55 import jdk.vm.ci.meta.JavaKind;
  56 import jdk.vm.ci.meta.JavaType;
  57 import jdk.vm.ci.meta.ResolvedJavaType;
  58 import jdk.vm.ci.meta.UnresolvedJavaType;
  59 import jdk.vm.ci.runtime.JVMCI;
  60 import jdk.vm.ci.runtime.JVMCIBackend;
  61 import jdk.vm.ci.runtime.JVMCICompiler;
  62 import jdk.vm.ci.runtime.JVMCICompilerFactory;
  63 import jdk.vm.ci.runtime.JVMCIRuntime;
  64 import jdk.vm.ci.services.JVMCIServiceLocator;
  65 import jdk.vm.ci.services.Services;
  66 
  67 /**
  68  * HotSpot implementation of a JVMCI runtime.
  69  */
  70 public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
  71 
  72     /**
  73      * Singleton instance lazily initialized via double-checked locking.
  74      */
  75     @NativeImageReinitialize private static volatile HotSpotJVMCIRuntime instance;
  76 
  77     private HotSpotResolvedObjectTypeImpl javaLangObject;
  78     private HotSpotResolvedObjectTypeImpl javaLangInvokeMethodHandle;
  79     private HotSpotResolvedObjectTypeImpl constantCallSiteType;
  80     private HotSpotResolvedObjectTypeImpl callSiteType;
  81     private HotSpotResolvedObjectTypeImpl javaLangString;
  82     private HotSpotResolvedObjectTypeImpl javaLangClass;
  83     private HotSpotResolvedObjectTypeImpl throwableType;
  84     private HotSpotResolvedObjectTypeImpl serializableType;
  85     private HotSpotResolvedObjectTypeImpl cloneableType;
  86     private HotSpotResolvedObjectTypeImpl enumType;
  87 
  88     HotSpotResolvedObjectTypeImpl getJavaLangObject() {
  89         if (javaLangObject == null) {
  90             javaLangObject = (HotSpotResolvedObjectTypeImpl) fromClass(Object.class);
  91         }
  92         return javaLangObject;
  93     }
  94 
  95     HotSpotResolvedObjectTypeImpl getJavaLangString() {
  96         if (javaLangString == null) {
  97             javaLangString = (HotSpotResolvedObjectTypeImpl) fromClass(String.class);
  98         }
  99         return javaLangString;
 100     }
 101 
 102     HotSpotResolvedObjectTypeImpl getJavaLangClass() {
 103         if (javaLangClass == null) {
 104             javaLangClass = (HotSpotResolvedObjectTypeImpl) fromClass(Class.class);
 105         }
 106         return javaLangClass;
 107     }
 108 
 109     HotSpotResolvedObjectTypeImpl getJavaLangCloneable() {
 110         if (cloneableType == null) {
 111             cloneableType = (HotSpotResolvedObjectTypeImpl) fromClass(Cloneable.class);
 112         }
 113         return cloneableType;
 114     }
 115 
 116     HotSpotResolvedObjectTypeImpl getJavaLangSerializable() {
 117         if (serializableType == null) {
 118             serializableType = (HotSpotResolvedObjectTypeImpl) fromClass(Serializable.class);
 119         }
 120         return serializableType;
 121     }
 122 
 123     HotSpotResolvedObjectTypeImpl getJavaLangThrowable() {
 124         if (throwableType == null) {
 125             throwableType = (HotSpotResolvedObjectTypeImpl) fromClass(Throwable.class);
 126         }
 127         return throwableType;
 128     }
 129 
 130     HotSpotResolvedObjectTypeImpl getJavaLangEnum() {
 131         if (enumType == null) {
 132             enumType = (HotSpotResolvedObjectTypeImpl) fromClass(Enum.class);
 133         }
 134         return enumType;
 135     }
 136 
 137     HotSpotResolvedObjectTypeImpl getConstantCallSite() {
 138         if (constantCallSiteType == null) {
 139             constantCallSiteType = (HotSpotResolvedObjectTypeImpl) fromClass(ConstantCallSite.class);
 140         }
 141         return constantCallSiteType;
 142     }
 143 
 144     HotSpotResolvedObjectTypeImpl getCallSite() {
 145         if (callSiteType == null) {
 146             callSiteType = (HotSpotResolvedObjectTypeImpl) fromClass(CallSite.class);
 147         }
 148         return callSiteType;
 149     }
 150 
 151     HotSpotResolvedObjectType getMethodHandleClass() {
 152         if (javaLangInvokeMethodHandle == null) {
 153             javaLangInvokeMethodHandle = (HotSpotResolvedObjectTypeImpl) fromClass(MethodHandle.class);
 154         }
 155         return javaLangInvokeMethodHandle;
 156     }
 157 
 158     /**
 159      * Gets the singleton {@link HotSpotJVMCIRuntime} object.
 160      */
 161     @VMEntryPoint
 162     @SuppressWarnings("try")
 163     public static HotSpotJVMCIRuntime runtime() {
 164         HotSpotJVMCIRuntime result = instance;
 165         if (result == null) {
 166             // Synchronize on JVMCI.class to avoid deadlock
 167             // between the two JVMCI initialization paths:
 168             // HotSpotJVMCIRuntime.runtime() and JVMCI.getRuntime().
 169             synchronized (JVMCI.class) {
 170                 result = instance;
 171                 if (result == null) {
 172                     try (InitTimer t = timer("HotSpotJVMCIRuntime.<init>")) {
 173                         instance = result = new HotSpotJVMCIRuntime();
 174 
 175                         // Can only do eager initialization of the JVMCI compiler
 176                         // once the singleton instance is available.
 177                         if (result.config.getFlag("EagerJVMCI", Boolean.class)) {
 178                             result.getCompiler();
 179                         }
 180                     }
 181                     // Ensures JVMCIRuntime::_HotSpotJVMCIRuntime_instance is
 182                     // initialized.
 183                     JVMCI.getRuntime();
 184                 }
 185                 // Make sure all the primitive box caches are populated (required to properly
 186                 // materialize boxed primitives
 187                 // during deoptimization).
 188                 Boolean.valueOf(false);
 189                 Byte.valueOf((byte) 0);
 190                 Short.valueOf((short) 0);
 191                 Character.valueOf((char) 0);
 192                 Integer.valueOf(0);
 193                 Long.valueOf(0);
 194             }
 195         }
 196         return result;
 197     }
 198 
 199     @VMEntryPoint
 200     static Throwable decodeThrowable(String encodedThrowable) throws Throwable {
 201         return TranslatedException.decodeThrowable(encodedThrowable);
 202     }
 203 
 204     @VMEntryPoint
 205     static String encodeThrowable(Throwable throwable) throws Throwable {
 206         return TranslatedException.encodeThrowable(throwable);
 207     }
 208 
 209     @VMEntryPoint
 210     static String callToString(Object o) {
 211         return o.toString();
 212     }
 213 
 214     /**
 215      * Set of recognized {@code "jvmci.*"} system properties. Entries not associated with an
 216      * {@link Option} have this object as their value.
 217      */
 218     static final Map<String, Object> options = new HashMap<>();
 219     static {
 220         options.put("jvmci.class.path.append", options);
 221     }
 222 
 223     /**
 224      * A list of all supported JVMCI options.
 225      */
 226     public enum Option {
 227         // @formatter:off
 228         Compiler(String.class, null, "Selects the system compiler. This must match the getCompilerName() value returned " +
 229                 "by a jdk.vm.ci.runtime.JVMCICompilerFactory provider. " +
 230                 "An empty string or the value \"null\" selects a compiler " +
 231                 "that will raise an exception upon receiving a compilation request."),
 232         // Note: The following one is not used (see InitTimer.ENABLED). It is added here
 233         // so that -XX:+JVMCIPrintProperties shows the option.
 234         InitTimer(Boolean.class, false, "Specifies if initialization timing is enabled."),
 235         PrintConfig(Boolean.class, false, "Prints VM configuration available via JVMCI."),
 236         AuditHandles(Boolean.class, false, "Record stack trace along with scoped foreign object reference wrappers " +
 237                 "to debug issue with a wrapper being used after its scope has closed."),
 238         TraceMethodDataFilter(String.class, null,
 239                 "Enables tracing of profiling info when read by JVMCI.",
 240                 "Empty value: trace all methods",
 241                         "Non-empty value: trace methods whose fully qualified name contains the value."),
 242         UseProfilingInformation(Boolean.class, true, "");
 243         // @formatter:on
 244 
 245         /**
 246          * The prefix for system properties that are JVMCI options.
 247          */
 248         private static final String JVMCI_OPTION_PROPERTY_PREFIX = "jvmci.";
 249 
 250         /**
 251          * Sentinel for value initialized to {@code null} since {@code null} means uninitialized.
 252          */
 253         private static final String NULL_VALUE = "NULL";
 254 
 255         private final Class<?> type;
 256         @NativeImageReinitialize private Object value;
 257         private final Object defaultValue;
 258         private boolean isDefault = true;
 259         private final String[] helpLines;
 260 
 261         Option(Class<?> type, Object defaultValue, String... helpLines) {
 262             assert Character.isUpperCase(name().charAt(0)) : "Option name must start with upper-case letter: " + name();
 263             this.type = type;
 264             this.defaultValue = defaultValue;
 265             this.helpLines = helpLines;
 266             Object existing = options.put(getPropertyName(), this);
 267             assert existing == null : getPropertyName();
 268         }
 269 
 270         @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel must be String since it's a static final in an enum")
 271         private void init(String propertyValue) {
 272             assert value == null : "cannot re-initialize " + name();
 273             if (propertyValue == null) {
 274                 this.value = defaultValue == null ? NULL_VALUE : defaultValue;
 275                 this.isDefault = true;
 276             } else {
 277                 if (type == Boolean.class) {
 278                     this.value = Boolean.parseBoolean(propertyValue);
 279                 } else if (type == String.class) {
 280                     this.value = propertyValue;
 281                 } else {
 282                     throw new JVMCIError("Unexpected option type " + type);
 283                 }
 284                 this.isDefault = false;
 285             }
 286         }
 287 
 288         @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel must be String since it's a static final in an enum")
 289         private Object getValue() {
 290             if (value == NULL_VALUE) {
 291                 return null;
 292             }
 293             if (value == null) {
 294                 return defaultValue;
 295             }
 296             return value;
 297         }
 298 
 299         /**
 300          * Gets the name of system property from which this option gets its value.
 301          */
 302         public String getPropertyName() {
 303             return JVMCI_OPTION_PROPERTY_PREFIX + name();
 304         }
 305 
 306         /**
 307          * Returns the option's value as boolean.
 308          *
 309          * @return option's value
 310          */
 311         public boolean getBoolean() {
 312             return (boolean) getValue();
 313         }
 314 
 315         /**
 316          * Returns the option's value as String.
 317          *
 318          * @return option's value
 319          */
 320         public String getString() {
 321             return (String) getValue();
 322         }
 323 
 324         private static final int PROPERTY_LINE_WIDTH = 80;
 325         private static final int PROPERTY_HELP_INDENT = 10;
 326 
 327         /**
 328          * Prints a description of the properties used to configure shared JVMCI code.
 329          *
 330          * @param out stream to print to
 331          */
 332         public static void printProperties(PrintStream out) {
 333             out.println("[JVMCI properties]");
 334             Option[] values = values();
 335             for (Option option : values) {
 336                 Object value = option.getValue();
 337                 if (value instanceof String) {
 338                     value = '"' + String.valueOf(value) + '"';
 339                 }
 340 
 341                 String name = option.getPropertyName();
 342                 String assign = option.isDefault ? "=" : ":=";
 343                 String typeName = option.type.getSimpleName();
 344                 String linePrefix = String.format("%s %s %s ", name, assign, value);
 345                 int typeStartPos = PROPERTY_LINE_WIDTH - typeName.length();
 346                 int linePad = typeStartPos - linePrefix.length();
 347                 if (linePad > 0) {
 348                     out.printf("%s%-" + linePad + "s[%s]%n", linePrefix, "", typeName);
 349                 } else {
 350                     out.printf("%s[%s]%n", linePrefix, typeName);
 351                 }
 352                 for (String line : option.helpLines) {
 353                     out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", line);
 354                 }
 355             }
 356         }
 357 
 358         /**
 359          * Compute string similarity based on Dice's coefficient.
 360          *
 361          * Ported from str_similar() in globals.cpp.
 362          */
 363         static float stringSimiliarity(String str1, String str2) {
 364             int hit = 0;
 365             for (int i = 0; i < str1.length() - 1; ++i) {
 366                 for (int j = 0; j < str2.length() - 1; ++j) {
 367                     if ((str1.charAt(i) == str2.charAt(j)) && (str1.charAt(i + 1) == str2.charAt(j + 1))) {
 368                         ++hit;
 369                         break;
 370                     }
 371                 }
 372             }
 373             return 2.0f * hit / (str1.length() + str2.length());
 374         }
 375 
 376         private static final float FUZZY_MATCH_THRESHOLD = 0.7F;
 377 
 378         /**
 379          * Parses all system properties starting with {@value #JVMCI_OPTION_PROPERTY_PREFIX} and
 380          * initializes the options based on their values.
 381          */
 382         static void parse() {
 383             Map<String, String> savedProps = jdk.vm.ci.services.Services.getSavedProperties();
 384             for (Map.Entry<String, String> e : savedProps.entrySet()) {
 385                 String name = e.getKey();
 386                 if (name.startsWith(Option.JVMCI_OPTION_PROPERTY_PREFIX)) {
 387                     Object value = options.get(name);
 388                     if (value == null) {
 389                         List<String> matches = new ArrayList<>();
 390                         for (String pn : options.keySet()) {
 391                             float score = stringSimiliarity(pn, name);
 392                             if (score >= FUZZY_MATCH_THRESHOLD) {
 393                                 matches.add(pn);
 394                             }
 395                         }
 396                         Formatter msg = new Formatter();
 397                         msg.format("Could not find option %s", name);
 398                         if (!matches.isEmpty()) {
 399                             msg.format("%nDid you mean one of the following?");
 400                             for (String match : matches) {
 401                                 msg.format("%n    %s=<value>", match);
 402                             }
 403                         }
 404                         throw new IllegalArgumentException(msg.toString());
 405                     } else if (value instanceof Option) {
 406                         Option option = (Option) value;
 407                         option.init(e.getValue());
 408                     }
 409                 }
 410             }
 411         }
 412     }
 413 
 414     private static HotSpotJVMCIBackendFactory findFactory(String architecture) {
 415         Iterable<HotSpotJVMCIBackendFactory> factories = getHotSpotJVMCIBackendFactories();
 416         assert factories != null : "sanity";
 417         for (HotSpotJVMCIBackendFactory factory : factories) {
 418             if (factory.getArchitecture().equalsIgnoreCase(architecture)) {
 419                 return factory;
 420             }
 421         }
 422 
 423         throw new JVMCIError("No JVMCI runtime available for the %s architecture", architecture);
 424     }
 425 
 426     private static volatile List<HotSpotJVMCIBackendFactory> cachedHotSpotJVMCIBackendFactories;
 427 
 428     @SuppressFBWarnings(value = "LI_LAZY_INIT_UPDATE_STATIC", justification = "not sure about this")
 429     private static Iterable<HotSpotJVMCIBackendFactory> getHotSpotJVMCIBackendFactories() {
 430         if (IS_IN_NATIVE_IMAGE || cachedHotSpotJVMCIBackendFactories != null) {
 431             return cachedHotSpotJVMCIBackendFactories;
 432         }
 433         Iterable<HotSpotJVMCIBackendFactory> result = ServiceLoader.load(HotSpotJVMCIBackendFactory.class, ClassLoader.getSystemClassLoader());
 434         if (IS_BUILDING_NATIVE_IMAGE) {
 435             cachedHotSpotJVMCIBackendFactories = new ArrayList<>();
 436             for (HotSpotJVMCIBackendFactory factory : result) {
 437                 cachedHotSpotJVMCIBackendFactories.add(factory);
 438             }
 439         }
 440         return result;
 441     }
 442 
 443     /**
 444      * Gets the kind of a word value on the {@linkplain #getHostJVMCIBackend() host} backend.
 445      */
 446     public static JavaKind getHostWordKind() {
 447         return runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordJavaKind;
 448     }
 449 
 450     protected final CompilerToVM compilerToVm;
 451 
 452     protected final HotSpotVMConfigStore configStore;
 453     protected final HotSpotVMConfig config;
 454     private final JVMCIBackend hostBackend;
 455 
 456     private final JVMCICompilerFactory compilerFactory;
 457     private final HotSpotJVMCICompilerFactory hsCompilerFactory;
 458     private volatile JVMCICompiler compiler;
 459     protected final HotSpotJVMCIReflection reflection;
 460 
 461     @NativeImageReinitialize private volatile boolean creatingCompiler;
 462 
 463     /**
 464      * Cache for speeding up {@link #fromClass(Class)}.
 465      */
 466     @NativeImageReinitialize private volatile ClassValue<WeakReferenceHolder<HotSpotResolvedJavaType>> resolvedJavaType;
 467 
 468     /**
 469      * To avoid calling ClassValue.remove to refresh the weak reference, which under certain
 470      * circumstances can lead to an infinite loop, we use a permanent holder with a mutable field
 471      * that we refresh.
 472      */
 473     private static class WeakReferenceHolder<T> {
 474         private volatile WeakReference<T> ref;
 475 
 476         WeakReferenceHolder(T value) {
 477             set(value);
 478         }
 479 
 480         void set(T value) {
 481             ref = new WeakReference<>(value);
 482         }
 483 
 484         T get() {
 485             return ref.get();
 486         }
 487     }
 488 
 489     @NativeImageReinitialize private HashMap<Long, WeakReference<ResolvedJavaType>> resolvedJavaTypes;
 490 
 491     /**
 492      * Stores the value set by {@link #excludeFromJVMCICompilation(Module...)} so that it can be
 493      * read from the VM.
 494      */
 495     @SuppressWarnings("unused")//
 496     @NativeImageReinitialize private Module[] excludeFromJVMCICompilation;
 497 
 498     private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>();
 499 
 500     private volatile List<HotSpotVMEventListener> vmEventListeners;
 501 
 502     private Iterable<HotSpotVMEventListener> getVmEventListeners() {
 503         if (vmEventListeners == null) {
 504             synchronized (this) {
 505                 if (vmEventListeners == null) {
 506                     vmEventListeners = JVMCIServiceLocator.getProviders(HotSpotVMEventListener.class);
 507                 }
 508             }
 509         }
 510         return vmEventListeners;
 511     }
 512 
 513     @SuppressWarnings("try")
 514     private HotSpotJVMCIRuntime() {
 515         compilerToVm = new CompilerToVM();
 516 
 517         try (InitTimer t = timer("HotSpotVMConfig<init>")) {
 518             configStore = new HotSpotVMConfigStore(compilerToVm);
 519             config = new HotSpotVMConfig(configStore);
 520         }
 521 
 522         reflection = IS_IN_NATIVE_IMAGE ? new SharedLibraryJVMCIReflection() : new HotSpotJDKReflection();
 523 
 524         PrintStream vmLogStream = null;
 525         if (IS_IN_NATIVE_IMAGE) {
 526             // Redirect System.out and System.err to HotSpot's TTY stream
 527             vmLogStream = new PrintStream(getLogStream());
 528             System.setOut(vmLogStream);
 529             System.setErr(vmLogStream);
 530         }
 531 
 532         // Initialize the Option values.
 533         Option.parse();
 534 
 535         String hostArchitecture = config.getHostArchitectureName();
 536 
 537         HotSpotJVMCIBackendFactory factory;
 538         try (InitTimer t = timer("find factory:", hostArchitecture)) {
 539             factory = findFactory(hostArchitecture);
 540         }
 541 
 542         try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) {
 543             hostBackend = registerBackend(factory.createJVMCIBackend(this, null));
 544         }
 545 
 546         compilerFactory = HotSpotJVMCICompilerConfig.getCompilerFactory();
 547         if (compilerFactory instanceof HotSpotJVMCICompilerFactory) {
 548             hsCompilerFactory = (HotSpotJVMCICompilerFactory) compilerFactory;
 549             if (hsCompilerFactory.getCompilationLevelAdjustment() != None) {
 550                 String name = HotSpotJVMCICompilerFactory.class.getName();
 551                 String msg = String.format("%s.getCompilationLevelAdjustment() is no longer supported. " +
 552                                 "Use %s.excludeFromJVMCICompilation() instead.", name, name);
 553                 throw new UnsupportedOperationException(msg);
 554             }
 555         } else {
 556             hsCompilerFactory = null;
 557         }
 558 
 559         if (config.getFlag("JVMCIPrintProperties", Boolean.class)) {
 560             if (vmLogStream == null) {
 561                 vmLogStream = new PrintStream(getLogStream());
 562             }
 563             Option.printProperties(vmLogStream);
 564             compilerFactory.printProperties(vmLogStream);
 565             System.exit(0);
 566         }
 567 
 568         if (Option.PrintConfig.getBoolean()) {
 569             configStore.printConfig();
 570         }
 571     }
 572 
 573     HotSpotResolvedJavaType createClass(Class<?> javaClass) {
 574         if (javaClass.isPrimitive()) {
 575             return HotSpotResolvedPrimitiveType.forKind(JavaKind.fromJavaClass(javaClass));
 576         }
 577         if (IS_IN_NATIVE_IMAGE) {
 578             try {
 579                 return compilerToVm.lookupType(javaClass.getName().replace('.', '/'), null, true);
 580             } catch (ClassNotFoundException e) {
 581                 throw new JVMCIError(e);
 582             }
 583         }
 584         return compilerToVm.lookupClass(javaClass);
 585     }
 586 
 587     private HotSpotResolvedJavaType fromClass0(Class<?> javaClass) {
 588         if (resolvedJavaType == null) {
 589             synchronized (this) {
 590                 if (resolvedJavaType == null) {
 591                     resolvedJavaType = new ClassValue<>() {
 592                         @Override
 593                         protected WeakReferenceHolder<HotSpotResolvedJavaType> computeValue(Class<?> type) {
 594                             return new WeakReferenceHolder<>(createClass(type));
 595                         }
 596                     };
 597                 }
 598             }
 599         }
 600 
 601         WeakReferenceHolder<HotSpotResolvedJavaType> ref = resolvedJavaType.get(javaClass);
 602         HotSpotResolvedJavaType javaType = ref.get();
 603             if (javaType == null) {
 604                 /*
 605              * If the referent has become null, create a new value and update cached weak reference.
 606                  */
 607             javaType = createClass(javaClass);
 608             ref.set(javaType);
 609         }
 610         return javaType;
 611     }
 612 
 613     /**
 614      * Gets the JVMCI mirror for a {@link Class} object.
 615      *
 616      * @return the {@link ResolvedJavaType} corresponding to {@code javaClass}
 617      */
 618     HotSpotResolvedJavaType fromClass(Class<?> javaClass) {
 619         if (javaClass == null) {
 620             return null;
 621         }
 622         return fromClass0(javaClass);
 623     }
 624 
 625     synchronized HotSpotResolvedObjectTypeImpl fromMetaspace(long klassPointer, String signature) {
 626         if (resolvedJavaTypes == null) {
 627             resolvedJavaTypes = new HashMap<>();
 628         }
 629         assert klassPointer != 0;
 630         WeakReference<ResolvedJavaType> klassReference = resolvedJavaTypes.get(klassPointer);
 631         HotSpotResolvedObjectTypeImpl javaType = null;
 632         if (klassReference != null) {
 633             javaType = (HotSpotResolvedObjectTypeImpl) klassReference.get();
 634         }
 635         if (javaType == null) {
 636             javaType = new HotSpotResolvedObjectTypeImpl(klassPointer, signature);
 637             resolvedJavaTypes.put(klassPointer, new WeakReference<>(javaType));
 638         }
 639         return javaType;
 640     }
 641 
 642     private JVMCIBackend registerBackend(JVMCIBackend backend) {
 643         Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass();
 644         JVMCIBackend oldValue = backends.put(arch, backend);
 645         assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName();
 646         return backend;
 647     }
 648 
 649     public HotSpotVMConfigStore getConfigStore() {
 650         return configStore;
 651     }
 652 
 653     public HotSpotVMConfig getConfig() {
 654         return config;
 655     }
 656 
 657     public CompilerToVM getCompilerToVM() {
 658         return compilerToVm;
 659     }
 660 
 661     HotSpotJVMCIReflection getReflection() {
 662         return reflection;
 663     }
 664 
 665     /**
 666      * Gets a predicate that determines if a given type can be considered trusted for the purpose of
 667      * intrinsifying methods it declares.
 668      *
 669      * @param compilerLeafClasses classes in the leaves of the module graph comprising the JVMCI
 670      *            compiler.
 671      */
 672     public Predicate<ResolvedJavaType> getIntrinsificationTrustPredicate(Class<?>... compilerLeafClasses) {
 673         return new Predicate<>() {
 674             @Override
 675             public boolean test(ResolvedJavaType type) {
 676                 if (type instanceof HotSpotResolvedObjectTypeImpl) {
 677                     HotSpotResolvedObjectTypeImpl hsType = (HotSpotResolvedObjectTypeImpl) type;
 678                     return compilerToVm.isTrustedForIntrinsics(hsType);
 679                 } else {
 680                     return false;
 681                 }
 682             }
 683         };
 684     }
 685 
 686     /**
 687      * Get the {@link Class} corresponding to {@code type}.
 688      *
 689      * @param type the type for which a {@link Class} is requested
 690      * @return the original Java class corresponding to {@code type} or {@code null} if this runtime
 691      *         does not support mapping {@link ResolvedJavaType} instances to {@link Class}
 692      *         instances
 693      */
 694     public Class<?> getMirror(ResolvedJavaType type) {
 695         if (type instanceof HotSpotResolvedJavaType && reflection instanceof HotSpotJDKReflection) {
 696             return ((HotSpotJDKReflection) reflection).getMirror((HotSpotResolvedJavaType) type);
 697         }
 698         return null;
 699     }
 700 
 701     @Override
 702     public JVMCICompiler getCompiler() {
 703         if (compiler == null) {
 704             synchronized (this) {
 705                 if (compiler == null) {
 706                     assert !creatingCompiler : "recursive compiler creation";
 707                     creatingCompiler = true;
 708                     compiler = compilerFactory.createCompiler(this);
 709                     creatingCompiler = false;
 710                 }
 711             }
 712         }
 713         return compiler;
 714     }
 715 
 716     /**
 717      * Converts a name to a Java type. This method attempts to resolve {@code name} to a
 718      * {@link ResolvedJavaType}.
 719      *
 720      * @param name a well formed Java type in {@linkplain JavaType#getName() internal} format
 721      * @param accessingType the context of resolution which must be non-null
 722      * @param resolve specifies whether resolution failure results in an unresolved type being
 723      *            return or a {@link LinkageError} being thrown
 724      * @return a Java type for {@code name} which is guaranteed to be of type
 725      *         {@link ResolvedJavaType} if {@code resolve == true}
 726      * @throws LinkageError if {@code resolve == true} and the resolution failed
 727      * @throws NullPointerException if {@code accessingClass} is {@code null}
 728      */
 729     public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 730         Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class");
 731         return lookupTypeInternal(name, accessingType, resolve);
 732     }
 733 
 734     JavaType lookupTypeInternal(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 735         // If the name represents a primitive type we can short-circuit the lookup.
 736         if (name.length() == 1) {
 737             JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
 738             return HotSpotResolvedPrimitiveType.forKind(kind);
 739         }
 740 
 741         // Resolve non-primitive types in the VM.
 742         HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType;
 743         try {
 744             final HotSpotResolvedJavaType klass = compilerToVm.lookupType(name, hsAccessingType, resolve);
 745 
 746             if (klass == null) {
 747                 assert resolve == false : name;
 748                 return UnresolvedJavaType.create(name);
 749             }
 750             return klass;
 751         } catch (ClassNotFoundException e) {
 752             throw (NoClassDefFoundError) new NoClassDefFoundError().initCause(e);
 753         }
 754     }
 755 
 756     @Override
 757     public JVMCIBackend getHostJVMCIBackend() {
 758         return hostBackend;
 759     }
 760 
 761     @Override
 762     public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) {
 763         assert arch != Architecture.class;
 764         return backends.get(arch);
 765     }
 766 
 767     public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() {
 768         return Collections.unmodifiableMap(backends);
 769     }
 770 
 771     @SuppressWarnings("try")
 772     @VMEntryPoint
 773     private HotSpotCompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long compileState, int id) {
 774         HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, compileState, id);
 775         CompilationRequestResult result = getCompiler().compileMethod(request);
 776         assert result != null : "compileMethod must always return something";
 777         HotSpotCompilationRequestResult hsResult;
 778         if (result instanceof HotSpotCompilationRequestResult) {
 779             hsResult = (HotSpotCompilationRequestResult) result;
 780         } else {
 781             Object failure = result.getFailure();
 782             if (failure != null) {
 783                 boolean retry = false; // Be conservative with unknown compiler
 784                 hsResult = HotSpotCompilationRequestResult.failure(failure.toString(), retry);
 785             } else {
 786                 int inlinedBytecodes = -1;
 787                 hsResult = HotSpotCompilationRequestResult.success(inlinedBytecodes);
 788             }
 789         }
 790         return hsResult;
 791     }
 792 
 793     /**
 794      * Guard to ensure shut down actions are performed at most once.
 795      */
 796     private boolean isShutdown;
 797 
 798     /**
 799      * Shuts down the runtime.
 800      */
 801     @VMEntryPoint
 802     private synchronized void shutdown() throws Exception {
 803         if (!isShutdown) {
 804             isShutdown = true;
 805             // Cleaners are normally only processed when a new Cleaner is
 806             // instantiated so process all remaining cleaners now.
 807             Cleaner.clean();
 808 
 809             for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 810                 vmEventListener.notifyShutdown();
 811             }
 812         }
 813     }
 814 
 815     /**
 816      * Notify on completion of a bootstrap.
 817      */
 818     @VMEntryPoint
 819     private void bootstrapFinished() throws Exception {
 820         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 821             vmEventListener.notifyBootstrapFinished();
 822         }
 823     }
 824 
 825     /**
 826      * Notify on successful install into the CodeCache.
 827      *
 828      * @param hotSpotCodeCacheProvider
 829      * @param installedCode
 830      * @param compiledCode
 831      */
 832     void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) {
 833         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 834             vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode);
 835         }
 836     }
 837 
 838     /**
 839      * Writes {@code length} bytes from {@code bytes} starting at offset {@code offset} to HotSpot's
 840      * log stream.
 841      *
 842      * @param flush specifies if the log stream should be flushed after writing
 843      * @param canThrow specifies if an error in the {@code bytes}, {@code offset} or {@code length}
 844      *            arguments should result in an exception or a negative return value. If
 845      *            {@code false}, this call will not perform any heap allocation
 846      * @return 0 on success, -1 if {@code bytes == null && !canThrow}, -2 if {@code !canThrow} and
 847      *         copying would cause access of data outside array bounds
 848      * @throws NullPointerException if {@code bytes == null}
 849      * @throws IndexOutOfBoundsException if copying would cause access of data outside array bounds
 850      */
 851     public int writeDebugOutput(byte[] bytes, int offset, int length, boolean flush, boolean canThrow) {
 852         return compilerToVm.writeDebugOutput(bytes, offset, length, flush, canThrow);
 853     }
 854 
 855     /**
 856      * Gets an output stream that writes to HotSpot's {@code tty} stream.
 857      */
 858     public OutputStream getLogStream() {
 859         return new OutputStream() {
 860 
 861             @Override
 862             public void write(byte[] b, int off, int len) throws IOException {
 863                 if (b == null) {
 864                     throw new NullPointerException();
 865                 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) {
 866                     throw new IndexOutOfBoundsException();
 867                 } else if (len == 0) {
 868                     return;
 869                 }
 870                 compilerToVm.writeDebugOutput(b, off, len, false, true);
 871             }
 872 
 873             @Override
 874             public void write(int b) throws IOException {
 875                 write(new byte[]{(byte) b}, 0, 1);
 876             }
 877 
 878             @Override
 879             public void flush() throws IOException {
 880                 compilerToVm.flushDebugOutput();
 881             }
 882         };
 883     }
 884 
 885     /**
 886      * Collects the current values of all JVMCI benchmark counters, summed up over all threads.
 887      */
 888     public long[] collectCounters() {
 889         return compilerToVm.collectCounters();
 890     }
 891 
 892     /**
 893      * @return the current number of per thread counters. May be set through
 894      *         {@code -XX:JVMCICompilerSize=} command line option or the
 895      *         {@link #setCountersSize(int)} call.
 896      */
 897     public int getCountersSize() {
 898         return compilerToVm.getCountersSize();
 899     }
 900 
 901     /**
 902      * Attempt to enlarge the number of per thread counters available. Requires a safepoint so
 903      * resizing should be rare to avoid performance effects.
 904      *
 905      * @param newSize
 906      * @return false if the resizing failed
 907      */
 908     public boolean setCountersSize(int newSize) {
 909         return compilerToVm.setCountersSize(newSize);
 910     }
 911 
 912     /**
 913      * The offset from the origin of an array to the first element.
 914      *
 915      * @return the offset in bytes
 916      */
 917     public int getArrayBaseOffset(JavaKind kind) {
 918         switch (kind) {
 919             case Boolean:
 920                 return compilerToVm.ARRAY_BOOLEAN_BASE_OFFSET;
 921             case Byte:
 922                 return compilerToVm.ARRAY_BYTE_BASE_OFFSET;
 923             case Char:
 924                 return compilerToVm.ARRAY_CHAR_BASE_OFFSET;
 925             case Short:
 926                 return compilerToVm.ARRAY_SHORT_BASE_OFFSET;
 927             case Int:
 928                 return compilerToVm.ARRAY_INT_BASE_OFFSET;
 929             case Long:
 930                 return compilerToVm.ARRAY_LONG_BASE_OFFSET;
 931             case Float:
 932                 return compilerToVm.ARRAY_FLOAT_BASE_OFFSET;
 933             case Double:
 934                 return compilerToVm.ARRAY_DOUBLE_BASE_OFFSET;
 935             case Object:
 936                 return compilerToVm.ARRAY_OBJECT_BASE_OFFSET;
 937             default:
 938                 throw new JVMCIError("%s", kind);
 939         }
 940 
 941     }
 942 
 943     /**
 944      * The scale used for the index when accessing elements of an array of this kind.
 945      *
 946      * @return the scale in order to convert the index into a byte offset
 947      */
 948     public int getArrayIndexScale(JavaKind kind) {
 949         switch (kind) {
 950             case Boolean:
 951                 return compilerToVm.ARRAY_BOOLEAN_INDEX_SCALE;
 952             case Byte:
 953                 return compilerToVm.ARRAY_BYTE_INDEX_SCALE;
 954             case Char:
 955                 return compilerToVm.ARRAY_CHAR_INDEX_SCALE;
 956             case Short:
 957                 return compilerToVm.ARRAY_SHORT_INDEX_SCALE;
 958             case Int:
 959                 return compilerToVm.ARRAY_INT_INDEX_SCALE;
 960             case Long:
 961                 return compilerToVm.ARRAY_LONG_INDEX_SCALE;
 962             case Float:
 963                 return compilerToVm.ARRAY_FLOAT_INDEX_SCALE;
 964             case Double:
 965                 return compilerToVm.ARRAY_DOUBLE_INDEX_SCALE;
 966             case Object:
 967                 return compilerToVm.ARRAY_OBJECT_INDEX_SCALE;
 968             default:
 969                 throw new JVMCIError("%s", kind);
 970 
 971         }
 972     }
 973 
 974     /**
 975      * Links each native method in {@code clazz} to an implementation in the JVMCI shared library.
 976      * <p>
 977      * A use case for this is a JVMCI compiler implementation that offers an API to Java code
 978      * executing in HotSpot to exercise functionality (mostly) in the JVMCI shared library. For
 979      * example:
 980      *
 981      * <pre>
 982      * package com.jcompile;
 983      *
 984      * import java.lang.reflect.Method;
 985      *
 986      * public static class JCompile {
 987      *     static {
 988      *         HotSpotJVMCIRuntime.runtime().registerNativeMethods(JCompile.class);
 989      *     }
 990      *     public static boolean compile(Method method, String[] options) {
 991      *         // Convert to simpler data types for passing/serializing across native interface
 992      *         long metaspaceMethodHandle = getHandle(method);
 993      *         char[] opts = convertToCharArray(options);
 994      *         return compile(metaspaceMethodHandle, opts);
 995      *     }
 996      *     private static native boolean compile0(long metaspaceMethodHandle, char[] options);
 997      *
 998      *     private static long getHandle(Method method) { ... }
 999      *     private static char[] convertToCharArray(String[] a) { ... }
1000      * }
1001      * </pre>
1002      *
1003      * The implementation of the native {@code JCompile.compile0} method would be in the JVMCI
1004      * shared library that contains the JVMCI compiler. The {@code JCompile.compile0} implementation
1005      * must be exported as the following JNI-compatible symbol:
1006      *
1007      * <pre>
1008      * Java_com_jcompile_JCompile_compile0
1009      * </pre>
1010      *
1011      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names"
1012      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#creating_the_vm"
1013      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#invocation_api_functions"
1014      *
1015      *
1016      * @return info about the Java VM in the JVMCI shared library {@code JavaVM*}. The info is
1017      *         encoded in a long array as follows:
1018      *
1019      *         <pre>
1020      *     long[] info = {
1021      *         javaVM, // the {@code JavaVM*} value
1022      *         javaVM->functions->reserved0,
1023      *         javaVM->functions->reserved1,
1024      *         javaVM->functions->reserved2
1025      *     }
1026      *         </pre>
1027      *
1028      * @throws NullPointerException if {@code clazz == null}
1029      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1030      *             {@code -XX:-UseJVMCINativeLibrary})
1031      * @throws IllegalStateException if the current execution context is the JVMCI shared library
1032      * @throws IllegalArgumentException if {@code clazz} is {@link Class#isPrimitive()}
1033      * @throws UnsatisfiedLinkError if there's a problem linking a native method in {@code clazz}
1034      *             (no matching JNI symbol or the native method is already linked to a different
1035      *             address)
1036      */
1037     public long[] registerNativeMethods(Class<?> clazz) {
1038         return compilerToVm.registerNativeMethods(clazz);
1039     }
1040 
1041     /**
1042      * Creates or retrieves an object in the peer runtime that mirrors {@code obj}. The types whose
1043      * objects can be translated are:
1044      * <ul>
1045      * <li>{@link HotSpotResolvedJavaMethodImpl},</li>
1046      * <li>{@link HotSpotResolvedObjectTypeImpl},</li>
1047      * <li>{@link HotSpotResolvedPrimitiveType},</li>
1048      * <li>{@link IndirectHotSpotObjectConstantImpl},</li>
1049      * <li>{@link DirectHotSpotObjectConstantImpl} and</li>
1050      * <li>{@link HotSpotNmethod}</li>
1051      * </ul>
1052      *
1053      * This mechanism can be used to pass and return values between the HotSpot and JVMCI shared
1054      * library runtimes. In the receiving runtime, the value can be converted back to an object with
1055      * {@link #unhand(Class, long)}.
1056      *
1057      * @param obj an object for which an equivalent instance in the peer runtime is requested
1058      * @return a JNI global reference to the mirror of {@code obj} in the peer runtime
1059      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1060      *             {@code -XX:-UseJVMCINativeLibrary})
1061      * @throws IllegalArgumentException if {@code obj} is not of a translatable type
1062      *
1063      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references"
1064      */
1065     public long translate(Object obj) {
1066         return compilerToVm.translate(obj);
1067     }
1068 
1069     /**
1070      * Dereferences and returns the object referred to by the JNI global reference {@code handle}.
1071      * The global reference is deleted prior to returning. Any further use of {@code handle} is
1072      * invalid.
1073      *
1074      * @param handle a JNI global reference to an object in the current runtime
1075      * @return the object referred to by {@code handle}
1076      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1077      *             {@code -XX:-UseJVMCINativeLibrary})
1078      * @throws ClassCastException if the returned object cannot be cast to {@code type}
1079      *
1080      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references"
1081      *
1082      */
1083     public <T> T unhand(Class<T> type, long handle) {
1084         return type.cast(compilerToVm.unhand(handle));
1085     }
1086 
1087     /**
1088      * Determines if the current thread is attached to the peer runtime.
1089      *
1090      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1091      *             {@code -XX:-UseJVMCINativeLibrary})
1092      * @throws IllegalStateException if the peer runtime has not been initialized
1093      */
1094     public boolean isCurrentThreadAttached() {
1095         return compilerToVm.isCurrentThreadAttached();
1096     }
1097 
1098     /**
1099      * Gets the address of the HotSpot {@code JavaThread} C++ object for the current thread. This
1100      * will return {@code 0} if called from an unattached JVMCI shared library thread.
1101      */
1102     public long getCurrentJavaThread() {
1103         return compilerToVm.getCurrentJavaThread();
1104     }
1105 
1106     /**
1107      * Ensures the current thread is attached to the peer runtime.
1108      *
1109      * @param asDaemon if the thread is not yet attached, should it be attached as a daemon
1110      * @return {@code true} if this call attached the current thread, {@code false} if the current
1111      *         thread was already attached
1112      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1113      *             {@code -XX:-UseJVMCINativeLibrary})
1114      * @throws IllegalStateException if the peer runtime has not been initialized or there is an
1115      *             error while trying to attach the thread
1116      * @throws ArrayIndexOutOfBoundsException if {@code javaVMInfo} is non-null and is shorter than
1117      *             the length of the array returned by {@link #registerNativeMethods}
1118      */
1119     public boolean attachCurrentThread(boolean asDaemon) {
1120         return compilerToVm.attachCurrentThread(asDaemon);
1121     }
1122 
1123     /**
1124      * Detaches the current thread from the peer runtime.
1125      *
1126      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1127      *             {@code -XX:-UseJVMCINativeLibrary})
1128      * @throws IllegalStateException if the peer runtime has not been initialized or if the current
1129      *             thread is not attached or if there is an error while trying to detach the thread
1130      */
1131     public void detachCurrentThread() {
1132         compilerToVm.detachCurrentThread();
1133     }
1134 
1135     /**
1136      * Informs HotSpot that no method whose module is in {@code modules} is to be compiled
1137      * with {@link #compileMethod}.
1138      *
1139      * @param modules the set of modules containing JVMCI compiler classes
1140      */
1141     public void excludeFromJVMCICompilation(Module...modules) {
1142         this.excludeFromJVMCICompilation = modules.clone();
1143     }
1144 
1145     /**
1146      * Calls {@link System#exit(int)} in HotSpot's runtime.
1147      */
1148     public void exitHotSpot(int status) {
1149         if (!IS_IN_NATIVE_IMAGE) {
1150             System.exit(status);
1151         }
1152         compilerToVm.callSystemExit(status);
1153     }
1154 }