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