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