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 
getJavaLangObject()87     HotSpotResolvedObjectTypeImpl getJavaLangObject() {
88         if (javaLangObject == null) {
89             javaLangObject = (HotSpotResolvedObjectTypeImpl) fromClass(Object.class);
90         }
91         return javaLangObject;
92     }
93 
getJavaLangString()94     HotSpotResolvedObjectTypeImpl getJavaLangString() {
95         if (javaLangString == null) {
96             javaLangString = (HotSpotResolvedObjectTypeImpl) fromClass(String.class);
97         }
98         return javaLangString;
99     }
100 
getJavaLangClass()101     HotSpotResolvedObjectTypeImpl getJavaLangClass() {
102         if (javaLangClass == null) {
103             javaLangClass = (HotSpotResolvedObjectTypeImpl) fromClass(Class.class);
104         }
105         return javaLangClass;
106     }
107 
getJavaLangCloneable()108     HotSpotResolvedObjectTypeImpl getJavaLangCloneable() {
109         if (cloneableType == null) {
110             cloneableType = (HotSpotResolvedObjectTypeImpl) fromClass(Cloneable.class);
111         }
112         return cloneableType;
113     }
114 
getJavaLangSerializable()115     HotSpotResolvedObjectTypeImpl getJavaLangSerializable() {
116         if (serializableType == null) {
117             serializableType = (HotSpotResolvedObjectTypeImpl) fromClass(Serializable.class);
118         }
119         return serializableType;
120     }
121 
getJavaLangThrowable()122     HotSpotResolvedObjectTypeImpl getJavaLangThrowable() {
123         if (throwableType == null) {
124             throwableType = (HotSpotResolvedObjectTypeImpl) fromClass(Throwable.class);
125         }
126         return throwableType;
127     }
128 
getJavaLangEnum()129     HotSpotResolvedObjectTypeImpl getJavaLangEnum() {
130         if (enumType == null) {
131             enumType = (HotSpotResolvedObjectTypeImpl) fromClass(Enum.class);
132         }
133         return enumType;
134     }
135 
getConstantCallSite()136     HotSpotResolvedObjectTypeImpl getConstantCallSite() {
137         if (constantCallSiteType == null) {
138             constantCallSiteType = (HotSpotResolvedObjectTypeImpl) fromClass(ConstantCallSite.class);
139         }
140         return constantCallSiteType;
141     }
142 
getCallSite()143     HotSpotResolvedObjectTypeImpl getCallSite() {
144         if (callSiteType == null) {
145             callSiteType = (HotSpotResolvedObjectTypeImpl) fromClass(CallSite.class);
146         }
147         return callSiteType;
148     }
149 
getMethodHandleClass()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")
runtime()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
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         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 
Option(Class<?> type, Object defaultValue, String... helpLines)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")
getValue()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          */
getPropertyName()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          */
getBoolean()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          */
getString()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          */
printProperties(PrintStream out)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 
findFactory(String architecture)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")
getHotSpotJVMCIBackendFactories()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      */
getHostWordKind()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 
WeakReferenceHolder(T value)401         WeakReferenceHolder(T value) {
402             set(value);
403         }
404 
set(T value)405         void set(T value) {
406             ref = new WeakReference<>(value);
407         }
408 
get()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 
getVmEventListeners()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")
HotSpotJVMCIRuntime()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 
createClass(Class<?> javaClass)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 
fromClass0(Class<?> javaClass)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      */
fromClass(Class<?> javaClass)540     HotSpotResolvedJavaType fromClass(Class<?> javaClass) {
541         if (javaClass == null) {
542             return null;
543         }
544         return fromClass0(javaClass);
545     }
546 
fromMetaspace(long klassPointer, String signature)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 
registerBackend(JVMCIBackend backend)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 
getConfigStore()571     public HotSpotVMConfigStore getConfigStore() {
572         return configStore;
573     }
574 
getConfig()575     public HotSpotVMConfig getConfig() {
576         return config;
577     }
578 
getCompilerToVM()579     public CompilerToVM getCompilerToVM() {
580         return compilerToVm;
581     }
582 
getReflection()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      */
getIntrinsificationTrustPredicate(Class<?>.... compilerLeafClasses)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      * 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      */
821     public void setCountersSize(int newSize) {
822         compilerToVm.setCountersSize(newSize);
823     }
824 
825     /**
826      * The offset from the origin of an array to the first element.
827      *
828      * @return the offset in bytes
829      */
830     public int getArrayBaseOffset(JavaKind kind) {
831         switch (kind) {
832             case Boolean:
833                 return compilerToVm.ARRAY_BOOLEAN_BASE_OFFSET;
834             case Byte:
835                 return compilerToVm.ARRAY_BYTE_BASE_OFFSET;
836             case Char:
837                 return compilerToVm.ARRAY_CHAR_BASE_OFFSET;
838             case Short:
839                 return compilerToVm.ARRAY_SHORT_BASE_OFFSET;
840             case Int:
841                 return compilerToVm.ARRAY_INT_BASE_OFFSET;
842             case Long:
843                 return compilerToVm.ARRAY_LONG_BASE_OFFSET;
844             case Float:
845                 return compilerToVm.ARRAY_FLOAT_BASE_OFFSET;
846             case Double:
847                 return compilerToVm.ARRAY_DOUBLE_BASE_OFFSET;
848             case Object:
849                 return compilerToVm.ARRAY_OBJECT_BASE_OFFSET;
850             default:
851                 throw new JVMCIError("%s", kind);
852         }
853 
854     }
855 
856     /**
857      * The scale used for the index when accessing elements of an array of this kind.
858      *
859      * @return the scale in order to convert the index into a byte offset
860      */
861     public int getArrayIndexScale(JavaKind kind) {
862         switch (kind) {
863             case Boolean:
864                 return compilerToVm.ARRAY_BOOLEAN_INDEX_SCALE;
865             case Byte:
866                 return compilerToVm.ARRAY_BYTE_INDEX_SCALE;
867             case Char:
868                 return compilerToVm.ARRAY_CHAR_INDEX_SCALE;
869             case Short:
870                 return compilerToVm.ARRAY_SHORT_INDEX_SCALE;
871             case Int:
872                 return compilerToVm.ARRAY_INT_INDEX_SCALE;
873             case Long:
874                 return compilerToVm.ARRAY_LONG_INDEX_SCALE;
875             case Float:
876                 return compilerToVm.ARRAY_FLOAT_INDEX_SCALE;
877             case Double:
878                 return compilerToVm.ARRAY_DOUBLE_INDEX_SCALE;
879             case Object:
880                 return compilerToVm.ARRAY_OBJECT_INDEX_SCALE;
881             default:
882                 throw new JVMCIError("%s", kind);
883 
884         }
885     }
886 
887     /**
888      * Links each native method in {@code clazz} to an implementation in the JVMCI shared library.
889      * <p>
890      * A use case for this is a JVMCI compiler implementation that offers an API to Java code
891      * executing in HotSpot to exercise functionality (mostly) in the JVMCI shared library. For
892      * example:
893      *
894      * <pre>
895      * package com.jcompile;
896      *
897      * import java.lang.reflect.Method;
898      *
899      * public static class JCompile {
900      *     static {
901      *         HotSpotJVMCIRuntime.runtime().registerNativeMethods(JCompile.class);
902      *     }
903      *     public static boolean compile(Method method, String[] options) {
904      *         // Convert to simpler data types for passing/serializing across native interface
905      *         long metaspaceMethodHandle = getHandle(method);
906      *         char[] opts = convertToCharArray(options);
907      *         return compile(metaspaceMethodHandle, opts);
908      *     }
909      *     private static native boolean compile0(long metaspaceMethodHandle, char[] options);
910      *
911      *     private static long getHandle(Method method) { ... }
912      *     private static char[] convertToCharArray(String[] a) { ... }
913      * }
914      * </pre>
915      *
916      * The implementation of the native {@code JCompile.compile0} method would be in the JVMCI
917      * shared library that contains the bulk of the JVMCI compiler. The {@code JCompile.compile0}
918      * implementation will be exported as the following JNI-compatible symbol:
919      *
920      * <pre>
921      * Java_com_jcompile_JCompile_compile0
922      * </pre>
923      *
924      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names"
925      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#creating_the_vm"
926      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#invocation_api_functions"
927      *
928      *
929      * @return an array of 4 longs where the first value is the {@code JavaVM*} value representing
930      *         the Java VM in the JVMCI shared library, and the remaining values are the first 3
931      *         pointers in the Invocation API function table (i.e., {@code JNIInvokeInterface})
932      * @throws NullPointerException if {@code clazz == null}
933      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
934      *             {@code -XX:-UseJVMCINativeLibrary})
935      * @throws IllegalStateException if the current execution context is the JVMCI shared library
936      * @throws IllegalArgumentException if {@code clazz} is {@link Class#isPrimitive()}
937      * @throws UnsatisfiedLinkError if there's a problem linking a native method in {@code clazz}
938      *             (no matching JNI symbol or the native method is already linked to a different
939      *             address)
940      */
941     public long[] registerNativeMethods(Class<?> clazz) {
942         return compilerToVm.registerNativeMethods(clazz);
943     }
944 
945     /**
946      * Creates or retrieves an object in the peer runtime that mirrors {@code obj}. The types whose
947      * objects can be translated are:
948      * <ul>
949      * <li>{@link HotSpotResolvedJavaMethodImpl},</li>
950      * <li>{@link HotSpotResolvedObjectTypeImpl},</li>
951      * <li>{@link HotSpotResolvedPrimitiveType},</li>
952      * <li>{@link IndirectHotSpotObjectConstantImpl},</li>
953      * <li>{@link DirectHotSpotObjectConstantImpl} and</li>
954      * <li>{@link HotSpotNmethod}</li>
955      * </ul>
956      *
957      * This mechanism can be used to pass and return values between the HotSpot and JVMCI shared
958      * library runtimes. In the receiving runtime, the value can be converted back to an object with
959      * {@link #unhand(Class, long)}.
960      *
961      * @param obj an object for which an equivalent instance in the peer runtime is requested
962      * @return a JNI global reference to the mirror of {@code obj} in the peer runtime
963      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
964      *             {@code -XX:-UseJVMCINativeLibrary})
965      * @throws IllegalArgumentException if {@code obj} is not of a translatable type
966      *
967      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references"
968      */
969     public long translate(Object obj) {
970         return compilerToVm.translate(obj);
971     }
972 
973     /**
974      * Dereferences and returns the object referred to by the JNI global reference {@code handle}.
975      * The global reference is deleted prior to returning. Any further use of {@code handle} is
976      * invalid.
977      *
978      * @param handle a JNI global reference to an object in the current runtime
979      * @return the object referred to by {@code handle}
980      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
981      *             {@code -XX:-UseJVMCINativeLibrary})
982      * @throws ClassCastException if the returned object cannot be cast to {@code type}
983      *
984      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references"
985      *
986      */
987     public <T> T unhand(Class<T> type, long handle) {
988         return type.cast(compilerToVm.unhand(handle));
989     }
990 
991     /**
992      * Determines if the current thread is attached to the peer runtime.
993      *
994      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
995      *             {@code -XX:-UseJVMCINativeLibrary})
996      * @throws IllegalStateException if the peer runtime has not been initialized
997      */
998     public boolean isCurrentThreadAttached() {
999         return compilerToVm.isCurrentThreadAttached();
1000     }
1001 
1002     /**
1003      * Gets the address of the HotSpot {@code JavaThread} C++ object for the current thread. This
1004      * will return {@code 0} if called from an unattached JVMCI shared library thread.
1005      */
1006     public long getCurrentJavaThread() {
1007         return compilerToVm.getCurrentJavaThread();
1008     }
1009 
1010     /**
1011      * Ensures the current thread is attached to the peer runtime.
1012      *
1013      * @param asDaemon if the thread is not yet attached, should it be attached as a daemon
1014      * @return {@code true} if this call attached the current thread, {@code false} if the current
1015      *         thread was already attached
1016      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1017      *             {@code -XX:-UseJVMCINativeLibrary})
1018      * @throws IllegalStateException if the peer runtime has not been initialized or there is an
1019      *             error while trying to attach the thread
1020      */
1021     public boolean attachCurrentThread(boolean asDaemon) {
1022         return compilerToVm.attachCurrentThread(asDaemon);
1023     }
1024 
1025     /**
1026      * Detaches the current thread from the peer runtime.
1027      *
1028      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1029      *             {@code -XX:-UseJVMCINativeLibrary})
1030      * @throws IllegalStateException if the peer runtime has not been initialized or if the current
1031      *             thread is not attached or if there is an error while trying to detach the thread
1032      */
1033     public void detachCurrentThread() {
1034         compilerToVm.detachCurrentThread();
1035     }
1036 
1037     /**
1038      * Informs HotSpot that no method whose module is in {@code modules} is to be compiled
1039      * with {@link #compileMethod}.
1040      *
1041      * @param modules the set of modules containing JVMCI compiler classes
1042      */
1043     public void excludeFromJVMCICompilation(Module...modules) {
1044         this.excludeFromJVMCICompilation = modules.clone();
1045     }
1046 
1047     /**
1048      * Calls {@link System#exit(int)} in HotSpot's runtime.
1049      */
1050     public void exitHotSpot(int status) {
1051         if (!IS_IN_NATIVE_IMAGE) {
1052             System.exit(status);
1053         }
1054         compilerToVm.callSystemExit(status);
1055     }
1056 }
1057