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.services.Services.IS_BUILDING_NATIVE_IMAGE;
  27 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
  28 
  29 import java.io.IOException;
  30 import java.io.OutputStream;
  31 import java.io.PrintStream;
  32 import java.io.Serializable;
  33 
  34 import java.lang.invoke.CallSite;
  35 import java.lang.invoke.ConstantCallSite;
  36 import java.lang.invoke.MethodHandle;
  37 import java.lang.module.ModuleDescriptor.Requires;
  38 import java.lang.ref.WeakReference;
  39 
  40 import java.util.ArrayList;
  41 import java.util.Collections;
  42 import java.util.HashMap;
  43 import java.util.HashSet;
  44 import java.util.List;
  45 import java.util.Map;
  46 import java.util.Objects;
  47 import java.util.ServiceLoader;
  48 import java.util.TreeMap;
  49 import java.util.function.Predicate;
  50 
  51 import jdk.internal.misc.Unsafe;
  52 
  53 import jdk.vm.ci.code.Architecture;
  54 import jdk.vm.ci.code.CompilationRequestResult;
  55 import jdk.vm.ci.code.CompiledCode;
  56 import jdk.vm.ci.code.InstalledCode;
  57 import jdk.vm.ci.common.InitTimer;
  58 import jdk.vm.ci.common.JVMCIError;
  59 import jdk.vm.ci.common.NativeImageReinitialize;
  60 import jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory.CompilationLevel;
  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 = Services.load(HotSpotJVMCIBackendFactory.class);
 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 result of {@link HotSpotJVMCICompilerFactory#getCompilationLevelAdjustment} so
 393      * that it can be read from the VM.
 394      */
 395     @SuppressWarnings("unused") private final int compilationLevelAdjustment;
 396 
 397     private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>();
 398 
 399     private volatile List<HotSpotVMEventListener> vmEventListeners;
 400 
 401     private Iterable<HotSpotVMEventListener> getVmEventListeners() {
 402         if (vmEventListeners == null) {
 403             synchronized (this) {
 404                 if (vmEventListeners == null) {
 405                     vmEventListeners = JVMCIServiceLocator.getProviders(HotSpotVMEventListener.class);
 406                 }
 407             }
 408         }
 409         return vmEventListeners;
 410     }
 411 
 412     @SuppressWarnings("try")
 413     private HotSpotJVMCIRuntime() {
 414         compilerToVm = new CompilerToVM();
 415 
 416         try (InitTimer t = timer("HotSpotVMConfig<init>")) {
 417             configStore = new HotSpotVMConfigStore(compilerToVm);
 418             config = new HotSpotVMConfig(configStore);
 419         }
 420 
 421         reflection = IS_IN_NATIVE_IMAGE ? new SharedLibraryJVMCIReflection() : new HotSpotJDKReflection();
 422 
 423         PrintStream vmLogStream = null;
 424         if (IS_IN_NATIVE_IMAGE) {
 425             // Redirect System.out and System.err to HotSpot's TTY stream
 426             vmLogStream = new PrintStream(getLogStream());
 427             System.setOut(vmLogStream);
 428             System.setErr(vmLogStream);
 429         }
 430 
 431         String hostArchitecture = config.getHostArchitectureName();
 432 
 433         HotSpotJVMCIBackendFactory factory;
 434         try (InitTimer t = timer("find factory:", hostArchitecture)) {
 435             factory = findFactory(hostArchitecture);
 436         }
 437 
 438         try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) {
 439             hostBackend = registerBackend(factory.createJVMCIBackend(this, null));
 440         }
 441 
 442         compilerFactory = HotSpotJVMCICompilerConfig.getCompilerFactory();
 443         if (compilerFactory instanceof HotSpotJVMCICompilerFactory) {
 444             hsCompilerFactory = (HotSpotJVMCICompilerFactory) compilerFactory;
 445             switch (hsCompilerFactory.getCompilationLevelAdjustment()) {
 446                 case None:
 447                     compilationLevelAdjustment = config.compLevelAdjustmentNone;
 448                     break;
 449                 case ByHolder:
 450                     compilationLevelAdjustment = config.compLevelAdjustmentByHolder;
 451                     break;
 452                 case ByFullSignature:
 453                     compilationLevelAdjustment = config.compLevelAdjustmentByFullSignature;
 454                     break;
 455                 default:
 456                     compilationLevelAdjustment = config.compLevelAdjustmentNone;
 457                     break;
 458             }
 459         } else {
 460             hsCompilerFactory = null;
 461             compilationLevelAdjustment = config.compLevelAdjustmentNone;
 462         }
 463 
 464         if (config.getFlag("JVMCIPrintProperties", Boolean.class)) {
 465             if (vmLogStream == null) {
 466                 vmLogStream = new PrintStream(getLogStream());
 467             }
 468             Option.printProperties(vmLogStream);
 469             compilerFactory.printProperties(vmLogStream);
 470             System.exit(0);
 471         }
 472 
 473         if (Option.PrintConfig.getBoolean()) {
 474             printConfig(configStore, compilerToVm);
 475         }
 476     }
 477 
 478     HotSpotResolvedJavaType createClass(Class<?> javaClass) {
 479         if (javaClass.isPrimitive()) {
 480             return HotSpotResolvedPrimitiveType.forKind(JavaKind.fromJavaClass(javaClass));
 481         }
 482         if (IS_IN_NATIVE_IMAGE) {
 483             try {
 484                 return compilerToVm.lookupType(javaClass.getName().replace('.', '/'), null, true);
 485             } catch (ClassNotFoundException e) {
 486                 throw new JVMCIError(e);
 487             }
 488         }
 489         return compilerToVm.lookupClass(javaClass);
 490     }
 491 
 492     private HotSpotResolvedJavaType fromClass0(Class<?> javaClass) {
 493         if (resolvedJavaType == null) {
 494             synchronized (this) {
 495                 if (resolvedJavaType == null) {
 496                     resolvedJavaType = new ClassValue<WeakReference<HotSpotResolvedJavaType>>() {
 497                         @Override
 498                         protected WeakReference<HotSpotResolvedJavaType> computeValue(Class<?> type) {
 499                             return new WeakReference<>(createClass(type));
 500                         }
 501                     };
 502                 }
 503             }
 504         }
 505         HotSpotResolvedJavaType javaType = null;
 506         while (javaType == null) {
 507             WeakReference<HotSpotResolvedJavaType> type = resolvedJavaType.get(javaClass);
 508             javaType = type.get();
 509             if (javaType == null) {
 510                 /*
 511                  * If the referent has become null, clear out the current value and let computeValue
 512                  * above create a new value. Reload the value in a loop because in theory the
 513                  * WeakReference referent can be reclaimed at any point.
 514                  */
 515                 resolvedJavaType.remove(javaClass);
 516             }
 517         }
 518         return javaType;
 519     }
 520 
 521     /**
 522      * Gets the JVMCI mirror for a {@link Class} object.
 523      *
 524      * @return the {@link ResolvedJavaType} corresponding to {@code javaClass}
 525      */
 526     HotSpotResolvedJavaType fromClass(Class<?> javaClass) {
 527         if (javaClass == null) {
 528             return null;
 529         }
 530         return fromClass0(javaClass);
 531     }
 532 
 533     synchronized HotSpotResolvedObjectTypeImpl fromMetaspace(long klassPointer, String signature) {
 534         if (resolvedJavaTypes == null) {
 535             resolvedJavaTypes = new HashMap<>();
 536         }
 537         assert klassPointer != 0;
 538         WeakReference<ResolvedJavaType> klassReference = resolvedJavaTypes.get(klassPointer);
 539         HotSpotResolvedObjectTypeImpl javaType = null;
 540         if (klassReference != null) {
 541             javaType = (HotSpotResolvedObjectTypeImpl) klassReference.get();
 542         }
 543         if (javaType == null) {
 544             javaType = new HotSpotResolvedObjectTypeImpl(klassPointer, signature);
 545             resolvedJavaTypes.put(klassPointer, new WeakReference<>(javaType));
 546         }
 547         return javaType;
 548     }
 549 
 550     private JVMCIBackend registerBackend(JVMCIBackend backend) {
 551         Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass();
 552         JVMCIBackend oldValue = backends.put(arch, backend);
 553         assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName();
 554         return backend;
 555     }
 556 
 557     public HotSpotVMConfigStore getConfigStore() {
 558         return configStore;
 559     }
 560 
 561     public HotSpotVMConfig getConfig() {
 562         return config;
 563     }
 564 
 565     public CompilerToVM getCompilerToVM() {
 566         return compilerToVm;
 567     }
 568 
 569     HotSpotJVMCIReflection getReflection() {
 570         return reflection;
 571     }
 572 
 573     /**
 574      * Gets a predicate that determines if a given type can be considered trusted for the purpose of
 575      * intrinsifying methods it declares.
 576      *
 577      * @param compilerLeafClasses classes in the leaves of the module graph comprising the JVMCI
 578      *            compiler.
 579      */
 580     public Predicate<ResolvedJavaType> getIntrinsificationTrustPredicate(Class<?>... compilerLeafClasses) {
 581         return new Predicate<ResolvedJavaType>() {
 582             @Override
 583             public boolean test(ResolvedJavaType type) {
 584                 if (type instanceof HotSpotResolvedObjectTypeImpl) {
 585                     HotSpotResolvedObjectTypeImpl hsType = (HotSpotResolvedObjectTypeImpl) type;
 586                     return compilerToVm.isTrustedForIntrinsics(hsType);
 587                 } else {
 588                     return false;
 589                 }
 590             }
 591         };
 592     }
 593 
 594     /**
 595      * Get the {@link Class} corresponding to {@code type}.
 596      *
 597      * @param type the type for which a {@link Class} is requested
 598      * @return the original Java class corresponding to {@code type} or {@code null} if this runtime
 599      *         does not support mapping {@link ResolvedJavaType} instances to {@link Class}
 600      *         instances
 601      */
 602     public Class<?> getMirror(ResolvedJavaType type) {
 603         if (type instanceof HotSpotResolvedJavaType && reflection instanceof HotSpotJDKReflection) {
 604             return ((HotSpotJDKReflection) reflection).getMirror((HotSpotResolvedJavaType) type);
 605         }
 606         return null;
 607     }
 608 
 609     @Override
 610     public JVMCICompiler getCompiler() {
 611         if (compiler == null) {
 612             synchronized (this) {
 613                 if (compiler == null) {
 614                     assert !creatingCompiler : "recursive compiler creation";
 615                     creatingCompiler = true;
 616                     compiler = compilerFactory.createCompiler(this);
 617                     creatingCompiler = false;
 618                 }
 619             }
 620         }
 621         return compiler;
 622     }
 623 
 624     /**
 625      * Converts a name to a Java type. This method attempts to resolve {@code name} to a
 626      * {@link ResolvedJavaType}.
 627      *
 628      * @param name a well formed Java type in {@linkplain JavaType#getName() internal} format
 629      * @param accessingType the context of resolution which must be non-null
 630      * @param resolve specifies whether resolution failure results in an unresolved type being
 631      *            return or a {@link LinkageError} being thrown
 632      * @return a Java type for {@code name} which is guaranteed to be of type
 633      *         {@link ResolvedJavaType} if {@code resolve == true}
 634      * @throws LinkageError if {@code resolve == true} and the resolution failed
 635      * @throws NullPointerException if {@code accessingClass} is {@code null}
 636      */
 637     public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 638         Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class");
 639         return lookupTypeInternal(name, accessingType, resolve);
 640     }
 641 
 642     JavaType lookupTypeInternal(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 643         // If the name represents a primitive type we can short-circuit the lookup.
 644         if (name.length() == 1) {
 645             JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
 646             return HotSpotResolvedPrimitiveType.forKind(kind);
 647         }
 648 
 649         // Resolve non-primitive types in the VM.
 650         HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType;
 651         try {
 652             final HotSpotResolvedJavaType klass = compilerToVm.lookupType(name, hsAccessingType, resolve);
 653 
 654             if (klass == null) {
 655                 assert resolve == false : name;
 656                 return UnresolvedJavaType.create(name);
 657             }
 658             return klass;
 659         } catch (ClassNotFoundException e) {
 660             throw (NoClassDefFoundError) new NoClassDefFoundError().initCause(e);
 661         }
 662     }
 663 
 664     @Override
 665     public JVMCIBackend getHostJVMCIBackend() {
 666         return hostBackend;
 667     }
 668 
 669     @Override
 670     public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) {
 671         assert arch != Architecture.class;
 672         return backends.get(arch);
 673     }
 674 
 675     public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() {
 676         return Collections.unmodifiableMap(backends);
 677     }
 678 
 679     @VMEntryPoint
 680     private int adjustCompilationLevel(Object declaringClass, String name, String signature, boolean isOsr, int level) {
 681         CompilationLevel curLevel;
 682         if (level == config.compilationLevelNone) {
 683             curLevel = CompilationLevel.None;
 684         } else if (level == config.compilationLevelSimple) {
 685             curLevel = CompilationLevel.Simple;
 686         } else if (level == config.compilationLevelLimitedProfile) {
 687             curLevel = CompilationLevel.LimitedProfile;
 688         } else if (level == config.compilationLevelFullProfile) {
 689             curLevel = CompilationLevel.FullProfile;
 690         } else if (level == config.compilationLevelFullOptimization) {
 691             curLevel = CompilationLevel.FullOptimization;
 692         } else {
 693             throw JVMCIError.shouldNotReachHere();
 694         }
 695 
 696         switch (hsCompilerFactory.adjustCompilationLevel(declaringClass, name, signature, isOsr, curLevel)) {
 697             case None:
 698                 return config.compilationLevelNone;
 699             case Simple:
 700                 return config.compilationLevelSimple;
 701             case LimitedProfile:
 702                 return config.compilationLevelLimitedProfile;
 703             case FullProfile:
 704                 return config.compilationLevelFullProfile;
 705             case FullOptimization:
 706                 return config.compilationLevelFullOptimization;
 707             default:
 708                 return level;
 709         }
 710     }
 711 
 712     @VMEntryPoint
 713     private HotSpotCompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long compileState, int id) {
 714         CompilationRequestResult result = getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, compileState, id));
 715         assert result != null : "compileMethod must always return something";
 716         HotSpotCompilationRequestResult hsResult;
 717         if (result instanceof HotSpotCompilationRequestResult) {
 718             hsResult = (HotSpotCompilationRequestResult) result;
 719         } else {
 720             Object failure = result.getFailure();
 721             if (failure != null) {
 722                 boolean retry = false; // Be conservative with unknown compiler
 723                 hsResult = HotSpotCompilationRequestResult.failure(failure.toString(), retry);
 724             } else {
 725                 int inlinedBytecodes = -1;
 726                 hsResult = HotSpotCompilationRequestResult.success(inlinedBytecodes);
 727             }
 728         }
 729 
 730         return hsResult;
 731     }
 732 
 733     /**
 734      * Shuts down the runtime.
 735      */
 736     @VMEntryPoint
 737     private void shutdown() throws Exception {
 738         // Cleaners are normally only processed when a new Cleaner is
 739         // instantiated so process all remaining cleaners now.
 740         Cleaner.clean();
 741 
 742         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 743             vmEventListener.notifyShutdown();
 744         }
 745     }
 746 
 747     /**
 748      * Notify on completion of a bootstrap.
 749      */
 750     @VMEntryPoint
 751     private void bootstrapFinished() throws Exception {
 752         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 753             vmEventListener.notifyBootstrapFinished();
 754         }
 755     }
 756 
 757     /**
 758      * Notify on successful install into the CodeCache.
 759      *
 760      * @param hotSpotCodeCacheProvider
 761      * @param installedCode
 762      * @param compiledCode
 763      */
 764     void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) {
 765         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 766             vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode);
 767         }
 768     }
 769 
 770     @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING", justification = "no localization here please!")
 771     private static void printConfigLine(CompilerToVM vm, String format, Object... args) {
 772         String line = String.format(format, args);
 773         byte[] lineBytes = line.getBytes();
 774         vm.writeDebugOutput(lineBytes, 0, lineBytes.length);
 775         vm.flushDebugOutput();
 776     }
 777 
 778     private static void printConfig(HotSpotVMConfigStore store, CompilerToVM vm) {
 779         TreeMap<String, VMField> fields = new TreeMap<>(store.getFields());
 780         for (VMField field : fields.values()) {
 781             if (!field.isStatic()) {
 782                 printConfigLine(vm, "[vmconfig:instance field] %s %s {offset=%d[0x%x]}%n", field.type, field.name, field.offset, field.offset);
 783             } else {
 784                 String value = field.value == null ? "null" : field.value instanceof Boolean ? field.value.toString() : String.format("%d[0x%x]", field.value, field.value);
 785                 printConfigLine(vm, "[vmconfig:static field] %s %s = %s {address=0x%x}%n", field.type, field.name, value, field.address);
 786             }
 787         }
 788         TreeMap<String, VMFlag> flags = new TreeMap<>(store.getFlags());
 789         for (VMFlag flag : flags.values()) {
 790             printConfigLine(vm, "[vmconfig:flag] %s %s = %s%n", flag.type, flag.name, flag.value);
 791         }
 792         TreeMap<String, Long> addresses = new TreeMap<>(store.getAddresses());
 793         for (Map.Entry<String, Long> e : addresses.entrySet()) {
 794             printConfigLine(vm, "[vmconfig:address] %s = %d[0x%x]%n", e.getKey(), e.getValue(), e.getValue());
 795         }
 796         TreeMap<String, Long> constants = new TreeMap<>(store.getConstants());
 797         for (Map.Entry<String, Long> e : constants.entrySet()) {
 798             printConfigLine(vm, "[vmconfig:constant] %s = %d[0x%x]%n", e.getKey(), e.getValue(), e.getValue());
 799         }
 800         for (VMIntrinsicMethod e : store.getIntrinsics()) {
 801             printConfigLine(vm, "[vmconfig:intrinsic] %d = %s.%s %s%n", e.id, e.declaringClass, e.name, e.descriptor);
 802         }
 803     }
 804 
 805     /**
 806      * Gets an output stream that writes to HotSpot's {@code tty} stream.
 807      */
 808     public OutputStream getLogStream() {
 809         return new OutputStream() {
 810 
 811             @Override
 812             public void write(byte[] b, int off, int len) throws IOException {
 813                 if (b == null) {
 814                     throw new NullPointerException();
 815                 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) {
 816                     throw new IndexOutOfBoundsException();
 817                 } else if (len == 0) {
 818                     return;
 819                 }
 820                 compilerToVm.writeDebugOutput(b, off, len);
 821             }
 822 
 823             @Override
 824             public void write(int b) throws IOException {
 825                 write(new byte[]{(byte) b}, 0, 1);
 826             }
 827 
 828             @Override
 829             public void flush() throws IOException {
 830                 compilerToVm.flushDebugOutput();
 831             }
 832         };
 833     }
 834 
 835     /**
 836      * Collects the current values of all JVMCI benchmark counters, summed up over all threads.
 837      */
 838     public long[] collectCounters() {
 839         return compilerToVm.collectCounters();
 840     }
 841 
 842     /**
 843      * The offset from the origin of an array to the first element.
 844      *
 845      * @return the offset in bytes
 846      */
 847     public int getArrayBaseOffset(JavaKind kind) {
 848         switch (kind) {
 849             case Boolean:
 850                 return compilerToVm.ARRAY_BOOLEAN_BASE_OFFSET;
 851             case Byte:
 852                 return compilerToVm.ARRAY_BYTE_BASE_OFFSET;
 853             case Char:
 854                 return compilerToVm.ARRAY_CHAR_BASE_OFFSET;
 855             case Short:
 856                 return compilerToVm.ARRAY_SHORT_BASE_OFFSET;
 857             case Int:
 858                 return compilerToVm.ARRAY_INT_BASE_OFFSET;
 859             case Long:
 860                 return compilerToVm.ARRAY_LONG_BASE_OFFSET;
 861             case Float:
 862                 return compilerToVm.ARRAY_FLOAT_BASE_OFFSET;
 863             case Double:
 864                 return compilerToVm.ARRAY_DOUBLE_BASE_OFFSET;
 865             case Object:
 866                 return compilerToVm.ARRAY_OBJECT_BASE_OFFSET;
 867             default:
 868                 throw new JVMCIError("%s", kind);
 869         }
 870 
 871     }
 872 
 873     /**
 874      * The scale used for the index when accessing elements of an array of this kind.
 875      *
 876      * @return the scale in order to convert the index into a byte offset
 877      */
 878     public int getArrayIndexScale(JavaKind kind) {
 879         switch (kind) {
 880             case Boolean:
 881                 return compilerToVm.ARRAY_BOOLEAN_INDEX_SCALE;
 882             case Byte:
 883                 return compilerToVm.ARRAY_BYTE_INDEX_SCALE;
 884             case Char:
 885                 return compilerToVm.ARRAY_CHAR_INDEX_SCALE;
 886             case Short:
 887                 return compilerToVm.ARRAY_SHORT_INDEX_SCALE;
 888             case Int:
 889                 return compilerToVm.ARRAY_INT_INDEX_SCALE;
 890             case Long:
 891                 return compilerToVm.ARRAY_LONG_INDEX_SCALE;
 892             case Float:
 893                 return compilerToVm.ARRAY_FLOAT_INDEX_SCALE;
 894             case Double:
 895                 return compilerToVm.ARRAY_DOUBLE_INDEX_SCALE;
 896             case Object:
 897                 return compilerToVm.ARRAY_OBJECT_INDEX_SCALE;
 898             default:
 899                 throw new JVMCIError("%s", kind);
 900 
 901         }
 902     }
 903 
 904     /**
 905      * Links each native method in {@code clazz} to an implementation in the JVMCI shared library.
 906      * <p>
 907      * A use case for this is a JVMCI compiler implementation that offers an API to Java code
 908      * executing in HotSpot to exercise functionality (mostly) in the JVMCI shared library. For
 909      * example:
 910      *
 911      * <pre>
 912      * package com.jcompile;
 913      *
 914      * import java.lang.reflect.Method;
 915      *
 916      * public static class JCompile {
 917      *     static {
 918      *         HotSpotJVMCIRuntime.runtime().registerNativeMethods(JCompile.class);
 919      *     }
 920      *     public static boolean compile(Method method, String[] options) {
 921      *         // Convert to simpler data types for passing/serializing across native interface
 922      *         long metaspaceMethodHandle = getHandle(method);
 923      *         char[] opts = convertToCharArray(options);
 924      *         return compile(metaspaceMethodHandle, opts);
 925      *     }
 926      *     private static native boolean compile0(long metaspaceMethodHandle, char[] options);
 927      *
 928      *     private static long getHandle(Method method) { ... }
 929      *     private static char[] convertToCharArray(String[] a) { ... }
 930      * }
 931      * </pre>
 932      *
 933      * The implementation of the native {@code JCompile.compile0} method would be in the JVMCI
 934      * shared library that contains the bulk of the JVMCI compiler. The {@code JCompile.compile0}
 935      * implementation will be exported as the following JNI-compatible symbol:
 936      *
 937      * <pre>
 938      * Java_com_jcompile_JCompile_compile0
 939      * </pre>
 940      *
 941      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names"
 942      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#creating_the_vm"
 943      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#invocation_api_functions"
 944      *
 945      *
 946      * @return an array of 4 longs where the first value is the {@code JavaVM*} value representing
 947      *         the Java VM in the JVMCI shared library, and the remaining values are the first 3
 948      *         pointers in the Invocation API function table (i.e., {@code JNIInvokeInterface})
 949      * @throws NullPointerException if {@code clazz == null}
 950      * @throws IllegalArgumentException if the current execution context is the JVMCI shared library
 951      *             or if {@code clazz} is {@link Class#isPrimitive()}
 952      * @throws UnsatisfiedLinkError if the JVMCI shared library is not available, a native method in
 953      *             {@code clazz} is already linked or the JVMCI shared library does not contain a
 954      *             JNI-compatible symbol for a native method in {@code clazz}
 955      */
 956     public long[] registerNativeMethods(Class<?> clazz) {
 957         return compilerToVm.registerNativeMethods(clazz);
 958     }
 959 
 960     /**
 961      * Creates or retrieves an object in the peer runtime that mirrors {@code obj}. The types whose
 962      * objects can be translated are:
 963      * <ul>
 964      * <li>{@link HotSpotResolvedJavaMethodImpl},</li>
 965      * <li>{@link HotSpotResolvedObjectTypeImpl},</li>
 966      * <li>{@link HotSpotResolvedPrimitiveType},</li>
 967      * <li>{@link IndirectHotSpotObjectConstantImpl},</li>
 968      * <li>{@link DirectHotSpotObjectConstantImpl} and</li>
 969      * <li>{@link HotSpotNmethod}</li>
 970      * </ul>
 971      *
 972      * This mechanism can be used to pass and return values between the HotSpot and JVMCI shared
 973      * library runtimes. In the receiving runtime, the value can be converted back to an object with
 974      * {@link #unhand(Class, long)}.
 975      *
 976      * @param obj an object for which an equivalent instance in the peer runtime is requested
 977      * @return a JNI global reference to the mirror of {@code obj} in the peer runtime
 978      * @throws IllegalArgumentException if {@code obj} is not of a translatable type
 979      *
 980      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references"
 981      */
 982     public long translate(Object obj) {
 983         return compilerToVm.translate(obj);
 984     }
 985 
 986     /**
 987      * Dereferences and returns the object referred to by the JNI global reference {@code handle}.
 988      * The global reference is deleted prior to returning. Any further use of {@code handle} is
 989      * invalid.
 990      *
 991      * @param handle a JNI global reference to an object in the current runtime
 992      * @return the object referred to by {@code handle}
 993      * @throws ClassCastException if the returned object cannot be case to {@code type}
 994      *
 995      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references"
 996      *
 997      */
 998     public <T> T unhand(Class<T> type, long handle) {
 999         return type.cast(compilerToVm.unhand(handle));
1000     }
1001 }