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