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 
getJavaLangObject()93     HotSpotResolvedObjectTypeImpl getJavaLangObject() {
94         if (javaLangObject == null) {
95             javaLangObject = (HotSpotResolvedObjectTypeImpl) fromClass(Object.class);
96         }
97         return javaLangObject;
98     }
99 
getJavaLangString()100     HotSpotResolvedObjectTypeImpl getJavaLangString() {
101         if (javaLangString == null) {
102             javaLangString = (HotSpotResolvedObjectTypeImpl) fromClass(String.class);
103         }
104         return javaLangString;
105     }
106 
getJavaLangClass()107     HotSpotResolvedObjectTypeImpl getJavaLangClass() {
108         if (javaLangClass == null) {
109             javaLangClass = (HotSpotResolvedObjectTypeImpl) fromClass(Class.class);
110         }
111         return javaLangClass;
112     }
113 
getJavaLangCloneable()114     HotSpotResolvedObjectTypeImpl getJavaLangCloneable() {
115         if (cloneableType == null) {
116             cloneableType = (HotSpotResolvedObjectTypeImpl) fromClass(Cloneable.class);
117         }
118         return cloneableType;
119     }
120 
getJavaLangSerializable()121     HotSpotResolvedObjectTypeImpl getJavaLangSerializable() {
122         if (serializableType == null) {
123             serializableType = (HotSpotResolvedObjectTypeImpl) fromClass(Serializable.class);
124         }
125         return serializableType;
126     }
127 
getJavaLangThrowable()128     HotSpotResolvedObjectTypeImpl getJavaLangThrowable() {
129         if (throwableType == null) {
130             throwableType = (HotSpotResolvedObjectTypeImpl) fromClass(Throwable.class);
131         }
132         return throwableType;
133     }
134 
getJavaLangEnum()135     HotSpotResolvedObjectTypeImpl getJavaLangEnum() {
136         if (enumType == null) {
137             enumType = (HotSpotResolvedObjectTypeImpl) fromClass(Enum.class);
138         }
139         return enumType;
140     }
141 
getConstantCallSite()142     HotSpotResolvedObjectTypeImpl getConstantCallSite() {
143         if (constantCallSiteType == null) {
144             constantCallSiteType = (HotSpotResolvedObjectTypeImpl) fromClass(ConstantCallSite.class);
145         }
146         return constantCallSiteType;
147     }
148 
getCallSite()149     HotSpotResolvedObjectTypeImpl getCallSite() {
150         if (callSiteType == null) {
151             callSiteType = (HotSpotResolvedObjectTypeImpl) fromClass(CallSite.class);
152         }
153         return callSiteType;
154     }
155 
getMethodHandleClass()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")
runtime()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
decodeThrowable(String encodedThrowable)199     static Throwable decodeThrowable(String encodedThrowable) throws Throwable {
200         return TranslatedException.decodeThrowable(encodedThrowable);
201     }
202 
203     @VMEntryPoint
encodeThrowable(Throwable throwable)204     static String encodeThrowable(Throwable throwable) throws Throwable {
205         return TranslatedException.encodeThrowable(throwable);
206     }
207 
208     @VMEntryPoint
callToString(Object o)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 
Option(Class<?> type, Object defaultValue, String... helpLines)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")
getValue()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          */
getPropertyName()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          */
getBoolean()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          */
getString()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          */
printProperties(PrintStream out)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 
findFactory(String architecture)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")
getHotSpotJVMCIBackendFactories()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      */
getHostWordKind()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;
WeakReferenceHolder(T value)398         WeakReferenceHolder(T value) {
399             set(value);
400         }
set(T value)401         void set(T value) {
402             ref = new WeakReference<T>(value);
403         }
get()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 
getVmEventListeners()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")
HotSpotJVMCIRuntime()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 
createClass(Class<?> javaClass)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 
fromClass0(Class<?> javaClass)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      */
fromClass(Class<?> javaClass)537     HotSpotResolvedJavaType fromClass(Class<?> javaClass) {
538         if (javaClass == null) {
539             return null;
540         }
541         return fromClass0(javaClass);
542     }
543 
fromMetaspace(long klassPointer, String signature)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 
registerBackend(JVMCIBackend backend)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 
getConfigStore()568     public HotSpotVMConfigStore getConfigStore() {
569         return configStore;
570     }
571 
getConfig()572     public HotSpotVMConfig getConfig() {
573         return config;
574     }
575 
getCompilerToVM()576     public CompilerToVM getCompilerToVM() {
577         return compilerToVm;
578     }
579 
getReflection()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      */
getIntrinsificationTrustPredicate(Class<?>.... compilerLeafClasses)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 }
1036