1 /*
   2  * Copyright (c) 2015, 2018, 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 
  27 import java.io.IOException;
  28 import java.io.OutputStream;
  29 import java.io.PrintStream;
  30 import java.lang.module.ModuleDescriptor.Requires;
  31 import java.util.Collections;
  32 import java.util.HashMap;
  33 import java.util.HashSet;
  34 import java.util.List;
  35 import java.util.Map;
  36 import java.util.Objects;
  37 import java.util.ServiceLoader;
  38 import java.util.Set;
  39 import java.util.TreeMap;
  40 import java.util.function.Predicate;
  41 
  42 import jdk.internal.misc.VM;
  43 import jdk.internal.misc.Unsafe;
  44 import jdk.vm.ci.code.Architecture;
  45 import jdk.vm.ci.code.CompilationRequestResult;
  46 import jdk.vm.ci.code.CompiledCode;
  47 import jdk.vm.ci.code.InstalledCode;
  48 import jdk.vm.ci.common.InitTimer;
  49 import jdk.vm.ci.common.JVMCIError;
  50 import jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory.CompilationLevel;
  51 import jdk.vm.ci.meta.JavaKind;
  52 import jdk.vm.ci.meta.JavaType;
  53 import jdk.vm.ci.meta.ResolvedJavaType;
  54 import jdk.vm.ci.meta.UnresolvedJavaType;
  55 import jdk.vm.ci.runtime.JVMCI;
  56 import jdk.vm.ci.runtime.JVMCIBackend;
  57 import jdk.vm.ci.runtime.JVMCICompiler;
  58 import jdk.vm.ci.runtime.JVMCICompilerFactory;
  59 import jdk.vm.ci.runtime.JVMCIRuntime;
  60 import jdk.vm.ci.services.JVMCIServiceLocator;
  61 
  62 /**
  63  * HotSpot implementation of a JVMCI runtime.
  64  *
  65  * The initialization of this class is very fragile since it's initialized both through
  66  * {@link JVMCI#initialize()} or through calling {@link HotSpotJVMCIRuntime#runtime()} and
  67  * {@link HotSpotJVMCIRuntime#runtime()} is also called by {@link JVMCI#initialize()}. So this class
  68  * can't have a static initializer and any required initialization must be done as part of
  69  * {@link #runtime()}. This allows the initialization to funnel back through
  70  * {@link JVMCI#initialize()} without deadlocking.
  71  */
  72 public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
  73 
  74     @SuppressWarnings("try")
  75     static class DelayedInit {
  76         private static final HotSpotJVMCIRuntime instance;
  77 
  78         static {
  79             try (InitTimer t = timer("HotSpotJVMCIRuntime.<init>")) {
  80                 instance = new HotSpotJVMCIRuntime();
  81 
  82                 // Can only do eager initialization of the JVMCI compiler
  83                 // once the singleton instance is available.
  84                 if (instance.config.getFlag("EagerJVMCI", Boolean.class)) {
  85                     instance.getCompiler();
  86                 }
  87             }
  88         }
  89     }
  90 
  91     /**
  92      * Gets the singleton {@link HotSpotJVMCIRuntime} object.
  93      */
  94     public static HotSpotJVMCIRuntime runtime() {
  95         JVMCI.initialize();
  96         return DelayedInit.instance;
  97     }
  98 
  99     /**
 100      * A list of all supported JVMCI options.
 101      */
 102     public enum Option {
 103         // @formatter:off
 104         Compiler(String.class, null, "Selects the system compiler. This must match the getCompilerName() value returned " +
 105                                      "by a jdk.vm.ci.runtime.JVMCICompilerFactory provider. " +
 106                                      "An empty string or the value \"null\" selects a compiler " +
 107                                      "that will raise an exception upon receiving a compilation request."),
 108         // Note: The following one is not used (see InitTimer.ENABLED). It is added here
 109         // so that -XX:+JVMCIPrintProperties shows the option.
 110         InitTimer(Boolean.class, false, "Specifies if initialization timing is enabled."),
 111         PrintConfig(Boolean.class, false, "Prints VM configuration available via JVMCI."),
 112         TraceMethodDataFilter(String.class, null,
 113                         "Enables tracing of profiling info when read by JVMCI.",
 114                         "Empty value: trace all methods",
 115                         "Non-empty value: trace methods whose fully qualified name contains the value."),
 116         UseProfilingInformation(Boolean.class, true, "");
 117         // @formatter:on
 118 
 119         /**
 120          * The prefix for system properties that are JVMCI options.
 121          */
 122         private static final String JVMCI_OPTION_PROPERTY_PREFIX = "jvmci.";
 123 
 124         /**
 125          * Marker for uninitialized flags.
 126          */
 127         private static final String UNINITIALIZED = "UNINITIALIZED";
 128 
 129         private final Class<?> type;
 130         private Object value;
 131         private final Object defaultValue;
 132         private boolean isDefault;
 133         private final String[] helpLines;
 134 
 135         Option(Class<?> type, Object defaultValue, String... helpLines) {
 136             assert Character.isUpperCase(name().charAt(0)) : "Option name must start with upper-case letter: " + name();
 137             this.type = type;
 138             this.value = UNINITIALIZED;
 139             this.defaultValue = defaultValue;
 140             this.helpLines = helpLines;
 141         }
 142 
 143         @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel must be String since it's a static final in an enum")
 144         private Object getValue() {
 145             if (value == UNINITIALIZED) {
 146                 String propertyValue = VM.getSavedProperty(getPropertyName());
 147                 if (propertyValue == null) {
 148                     this.value = defaultValue;
 149                     this.isDefault = true;
 150                 } else {
 151                     if (type == Boolean.class) {
 152                         this.value = Boolean.parseBoolean(propertyValue);
 153                     } else if (type == String.class) {
 154                         this.value = propertyValue;
 155                     } else {
 156                         throw new JVMCIError("Unexpected option type " + type);
 157                     }
 158                     this.isDefault = false;
 159                 }
 160                 // Saved properties should not be interned - let's be sure
 161                 assert value != UNINITIALIZED;
 162             }
 163             return value;
 164         }
 165 
 166         /**
 167          * Gets the name of system property from which this option gets its value.
 168          */
 169         public String getPropertyName() {
 170             return JVMCI_OPTION_PROPERTY_PREFIX + name();
 171         }
 172 
 173         /**
 174          * Returns the option's value as boolean.
 175          *
 176          * @return option's value
 177          */
 178         public boolean getBoolean() {
 179             return (boolean) getValue();
 180         }
 181 
 182         /**
 183          * Returns the option's value as String.
 184          *
 185          * @return option's value
 186          */
 187         public String getString() {
 188             return (String) getValue();
 189         }
 190 
 191         private static final int PROPERTY_LINE_WIDTH = 80;
 192         private static final int PROPERTY_HELP_INDENT = 10;
 193 
 194         /**
 195          * Prints a description of the properties used to configure shared JVMCI code.
 196          *
 197          * @param out stream to print to
 198          */
 199         public static void printProperties(PrintStream out) {
 200             out.println("[JVMCI properties]");
 201             Option[] values = values();
 202             for (Option option : values) {
 203                 Object value = option.getValue();
 204                 if (value instanceof String) {
 205                     value = '"' + String.valueOf(value) + '"';
 206                 }
 207 
 208                 String name = option.getPropertyName();
 209                 String assign = option.isDefault ? "=" : ":=";
 210                 String typeName = option.type.getSimpleName();
 211                 String linePrefix = String.format("%s %s %s ", name, assign, value);
 212                 int typeStartPos = PROPERTY_LINE_WIDTH - typeName.length();
 213                 int linePad = typeStartPos - linePrefix.length();
 214                 if (linePad > 0) {
 215                     out.printf("%s%-" + linePad + "s[%s]%n", linePrefix, "", typeName);
 216                 } else {
 217                     out.printf("%s[%s]%n", linePrefix, typeName);
 218                 }
 219                 for (String line : option.helpLines) {
 220                     out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", line);
 221                 }
 222             }
 223         }
 224     }
 225 
 226     static HotSpotJVMCIBackendFactory findFactory(String architecture) {
 227         for (HotSpotJVMCIBackendFactory factory : ServiceLoader.load(HotSpotJVMCIBackendFactory.class, ClassLoader.getSystemClassLoader())) {
 228             if (factory.getArchitecture().equalsIgnoreCase(architecture)) {
 229                 return factory;
 230             }
 231         }
 232 
 233         throw new JVMCIError("No JVMCI runtime available for the %s architecture", architecture);
 234     }
 235 
 236     /**
 237      * Gets the kind of a word value on the {@linkplain #getHostJVMCIBackend() host} backend.
 238      */
 239     public static JavaKind getHostWordKind() {
 240         return runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordJavaKind;
 241     }
 242 
 243     protected final CompilerToVM compilerToVm;
 244 
 245     protected final HotSpotVMConfigStore configStore;
 246     protected final HotSpotVMConfig config;
 247     private final JVMCIBackend hostBackend;
 248 
 249     private final JVMCICompilerFactory compilerFactory;
 250     private final HotSpotJVMCICompilerFactory hsCompilerFactory;
 251     private volatile JVMCICompiler compiler;
 252     protected final HotSpotJVMCIMetaAccessContext metaAccessContext;
 253 
 254     /**
 255      * Stores the result of {@link HotSpotJVMCICompilerFactory#getCompilationLevelAdjustment} so
 256      * that it can be read from the VM.
 257      */
 258     @SuppressWarnings("unused") private final int compilationLevelAdjustment;
 259 
 260     private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>();
 261 
 262     private volatile List<HotSpotVMEventListener> vmEventListeners;
 263 
 264     private Iterable<HotSpotVMEventListener> getVmEventListeners() {
 265         if (vmEventListeners == null) {
 266             synchronized (this) {
 267                 if (vmEventListeners == null) {
 268                     vmEventListeners = JVMCIServiceLocator.getProviders(HotSpotVMEventListener.class);
 269                 }
 270             }
 271         }
 272         return vmEventListeners;
 273     }
 274 
 275     @SuppressWarnings("try")
 276     private HotSpotJVMCIRuntime() {
 277         compilerToVm = new CompilerToVM();
 278 
 279         try (InitTimer t = timer("HotSpotVMConfig<init>")) {
 280             configStore = new HotSpotVMConfigStore(compilerToVm);
 281             config = new HotSpotVMConfig(configStore);
 282         }
 283 
 284         String hostArchitecture = config.getHostArchitectureName();
 285 
 286         HotSpotJVMCIBackendFactory factory;
 287         try (InitTimer t = timer("find factory:", hostArchitecture)) {
 288             factory = findFactory(hostArchitecture);
 289         }
 290 
 291         try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) {
 292             hostBackend = registerBackend(factory.createJVMCIBackend(this, null));
 293         }
 294 
 295         metaAccessContext = new HotSpotJVMCIMetaAccessContext();
 296 
 297         compilerFactory = HotSpotJVMCICompilerConfig.getCompilerFactory();
 298         if (compilerFactory instanceof HotSpotJVMCICompilerFactory) {
 299             hsCompilerFactory = (HotSpotJVMCICompilerFactory) compilerFactory;
 300             switch (hsCompilerFactory.getCompilationLevelAdjustment()) {
 301                 case None:
 302                     compilationLevelAdjustment = config.compLevelAdjustmentNone;
 303                     break;
 304                 case ByHolder:
 305                     compilationLevelAdjustment = config.compLevelAdjustmentByHolder;
 306                     break;
 307                 case ByFullSignature:
 308                     compilationLevelAdjustment = config.compLevelAdjustmentByFullSignature;
 309                     break;
 310                 default:
 311                     compilationLevelAdjustment = config.compLevelAdjustmentNone;
 312                     break;
 313             }
 314         } else {
 315             hsCompilerFactory = null;
 316             compilationLevelAdjustment = config.compLevelAdjustmentNone;
 317         }
 318 
 319         if (config.getFlag("JVMCIPrintProperties", Boolean.class)) {
 320             PrintStream out = new PrintStream(getLogStream());
 321             Option.printProperties(out);
 322             compilerFactory.printProperties(out);
 323             System.exit(0);
 324         }
 325 
 326         if (Option.PrintConfig.getBoolean()) {
 327             printConfig(configStore, compilerToVm);
 328         }
 329     }
 330 
 331     private JVMCIBackend registerBackend(JVMCIBackend backend) {
 332         Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass();
 333         JVMCIBackend oldValue = backends.put(arch, backend);
 334         assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName();
 335         return backend;
 336     }
 337 
 338     ResolvedJavaType fromClass(Class<?> javaClass) {
 339         return metaAccessContext.fromClass(javaClass);
 340     }
 341 
 342     public HotSpotVMConfigStore getConfigStore() {
 343         return configStore;
 344     }
 345 
 346     public HotSpotVMConfig getConfig() {
 347         return config;
 348     }
 349 
 350     public CompilerToVM getCompilerToVM() {
 351         return compilerToVm;
 352     }
 353 
 354     // Non-volatile since multi-initialization is harmless
 355     private Predicate<ResolvedJavaType> intrinsificationTrustPredicate;
 356 
 357     /**
 358      * Gets a predicate that determines if a given type can be considered trusted for the purpose of
 359      * intrinsifying methods it declares.
 360      *
 361      * @param compilerLeafClasses classes in the leaves of the module graph comprising the JVMCI
 362      *            compiler.
 363      */
 364     public Predicate<ResolvedJavaType> getIntrinsificationTrustPredicate(Class<?>... compilerLeafClasses) {
 365         if (intrinsificationTrustPredicate == null) {
 366             intrinsificationTrustPredicate = new Predicate<>() {
 367                 @Override
 368                 public boolean test(ResolvedJavaType type) {
 369                     if (type instanceof HotSpotResolvedJavaType) {
 370                         Class<?> mirror = getMirror(type);
 371                         Module module = mirror.getModule();
 372                         return getTrustedModules().contains(module);
 373                     } else {
 374                         return false;
 375                     }
 376                 }
 377 
 378                 private volatile Set<Module> trustedModules;
 379 
 380                 private Set<Module> getTrustedModules() {
 381                     Set<Module> modules = trustedModules;
 382                     if (modules == null) {
 383                         modules = new HashSet<>();
 384                         for (Class<?> compilerConfiguration : compilerLeafClasses) {
 385                             Module compilerConfigurationModule = compilerConfiguration.getModule();
 386                             if (compilerConfigurationModule.getDescriptor().isAutomatic()) {
 387                                 throw new IllegalArgumentException(String.format("The module '%s' defining the Graal compiler configuration class '%s' must not be an automatic module",
 388                                                 compilerConfigurationModule.getName(), compilerConfiguration.getClass().getName()));
 389                             }
 390                             modules.add(compilerConfigurationModule);
 391                             for (Requires require : compilerConfigurationModule.getDescriptor().requires()) {
 392                                 for (Module module : compilerConfigurationModule.getLayer().modules()) {
 393                                     if (module.getName().equals(require.name())) {
 394                                         modules.add(module);
 395                                     }
 396                                 }
 397                             }
 398                         }
 399                         trustedModules = modules;
 400                     }
 401                     return modules;
 402                 }
 403             };
 404         }
 405         return intrinsificationTrustPredicate;
 406     }
 407 
 408     /**
 409      * Get the {@link Class} corresponding to {@code type}.
 410      *
 411      * @param type the type for which a {@link Class} is requested
 412      * @return the original Java class corresponding to {@code type} or {@code null} if this runtime
 413      *         does not support mapping {@link ResolvedJavaType} instances to {@link Class}
 414      *         instances
 415      */
 416     @SuppressWarnings("static-method")
 417     public Class<?> getMirror(ResolvedJavaType type) {
 418         return ((HotSpotResolvedJavaType) type).mirror();
 419     }
 420 
 421     @Override
 422     public JVMCICompiler getCompiler() {
 423         if (compiler == null) {
 424             synchronized (this) {
 425                 if (compiler == null) {
 426                     compiler = compilerFactory.createCompiler(this);
 427                 }
 428             }
 429         }
 430         return compiler;
 431     }
 432 
 433     /**
 434      * Converts a name to a Java type. This method attempts to resolve {@code name} to a
 435      * {@link ResolvedJavaType}.
 436      *
 437      * @param name a well formed Java type in {@linkplain JavaType#getName() internal} format
 438      * @param accessingType the context of resolution which must be non-null
 439      * @param resolve specifies whether resolution failure results in an unresolved type being
 440      *            return or a {@link LinkageError} being thrown
 441      * @return a Java type for {@code name} which is guaranteed to be of type
 442      *         {@link ResolvedJavaType} if {@code resolve == true}
 443      * @throws LinkageError if {@code resolve == true} and the resolution failed
 444      * @throws NullPointerException if {@code accessingClass} is {@code null}
 445      */
 446     public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 447         Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class");
 448         // If the name represents a primitive type we can short-circuit the lookup.
 449         if (name.length() == 1) {
 450             JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
 451             return fromClass(kind.toJavaClass());
 452         }
 453 
 454         // Resolve non-primitive types in the VM.
 455         HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType;
 456         try {
 457             final HotSpotResolvedObjectTypeImpl klass = compilerToVm.lookupType(name, hsAccessingType.mirror(), resolve);
 458 
 459             if (klass == null) {
 460                 assert resolve == false;
 461                 return UnresolvedJavaType.create(name);
 462             }
 463             return klass;
 464         } catch (ClassNotFoundException e) {
 465             throw (NoClassDefFoundError) new NoClassDefFoundError().initCause(e);
 466         }
 467     }
 468 
 469     @Override
 470     public JVMCIBackend getHostJVMCIBackend() {
 471         return hostBackend;
 472     }
 473 
 474     @Override
 475     public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) {
 476         assert arch != Architecture.class;
 477         return backends.get(arch);
 478     }
 479 
 480     public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() {
 481         return Collections.unmodifiableMap(backends);
 482     }
 483 
 484     /**
 485      * Called from the VM.
 486      */
 487     @SuppressWarnings({"unused"})
 488     private int adjustCompilationLevel(Class<?> declaringClass, String name, String signature, boolean isOsr, int level) {
 489         CompilationLevel curLevel;
 490         if (level == config.compilationLevelNone) {
 491             curLevel = CompilationLevel.None;
 492         } else if (level == config.compilationLevelSimple) {
 493             curLevel = CompilationLevel.Simple;
 494         } else if (level == config.compilationLevelLimitedProfile) {
 495             curLevel = CompilationLevel.LimitedProfile;
 496         } else if (level == config.compilationLevelFullProfile) {
 497             curLevel = CompilationLevel.FullProfile;
 498         } else if (level == config.compilationLevelFullOptimization) {
 499             curLevel = CompilationLevel.FullOptimization;
 500         } else {
 501             throw JVMCIError.shouldNotReachHere();
 502         }
 503 
 504         switch (hsCompilerFactory.adjustCompilationLevel(declaringClass, name, signature, isOsr, curLevel)) {
 505             case None:
 506                 return config.compilationLevelNone;
 507             case Simple:
 508                 return config.compilationLevelSimple;
 509             case LimitedProfile:
 510                 return config.compilationLevelLimitedProfile;
 511             case FullProfile:
 512                 return config.compilationLevelFullProfile;
 513             case FullOptimization:
 514                 return config.compilationLevelFullOptimization;
 515             default:
 516                 return level;
 517         }
 518     }
 519 
 520     /**
 521      * Called from the VM.
 522      */
 523     @SuppressWarnings({"unused"})
 524     private HotSpotCompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) {
 525         CompilationRequestResult result = getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, jvmciEnv, id));
 526         assert result != null : "compileMethod must always return something";
 527         HotSpotCompilationRequestResult hsResult;
 528         if (result instanceof HotSpotCompilationRequestResult) {
 529             hsResult = (HotSpotCompilationRequestResult) result;
 530         } else {
 531             Object failure = result.getFailure();
 532             if (failure != null) {
 533                 boolean retry = false; // Be conservative with unknown compiler
 534                 hsResult = HotSpotCompilationRequestResult.failure(failure.toString(), retry);
 535             } else {
 536                 int inlinedBytecodes = -1;
 537                 hsResult = HotSpotCompilationRequestResult.success(inlinedBytecodes);
 538             }
 539         }
 540 
 541         return hsResult;
 542     }
 543 
 544     /**
 545      * Shuts down the runtime.
 546      *
 547      * Called from the VM.
 548      */
 549     @SuppressWarnings({"unused"})
 550     private void shutdown() throws Exception {
 551         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 552             vmEventListener.notifyShutdown();
 553         }
 554     }
 555 
 556     /**
 557      * Notify on completion of a bootstrap.
 558      *
 559      * Called from the VM.
 560      */
 561     @SuppressWarnings({"unused"})
 562     private void bootstrapFinished() throws Exception {
 563         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 564             vmEventListener.notifyBootstrapFinished();
 565         }
 566     }
 567 
 568     /**
 569      * Notify on successful install into the CodeCache.
 570      *
 571      * @param hotSpotCodeCacheProvider
 572      * @param installedCode
 573      * @param compiledCode
 574      */
 575     void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) {
 576         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 577             vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode);
 578         }
 579     }
 580 
 581     @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING", justification = "no localization here please!")
 582     private static void printConfigLine(CompilerToVM vm, String format, Object... args) {
 583         String line = String.format(format, args);
 584         byte[] lineBytes = line.getBytes();
 585         vm.writeDebugOutput(lineBytes, 0, lineBytes.length);
 586         vm.flushDebugOutput();
 587     }
 588 
 589     private static void printConfig(HotSpotVMConfigStore store, CompilerToVM vm) {
 590         TreeMap<String, VMField> fields = new TreeMap<>(store.getFields());
 591         for (VMField field : fields.values()) {
 592             if (!field.isStatic()) {
 593                 printConfigLine(vm, "[vmconfig:instance field] %s %s {offset=%d[0x%x]}%n", field.type, field.name, field.offset, field.offset);
 594             } else {
 595                 String value = field.value == null ? "null" : field.value instanceof Boolean ? field.value.toString() : String.format("%d[0x%x]", field.value, field.value);
 596                 printConfigLine(vm, "[vmconfig:static field] %s %s = %s {address=0x%x}%n", field.type, field.name, value, field.address);
 597             }
 598         }
 599         TreeMap<String, VMFlag> flags = new TreeMap<>(store.getFlags());
 600         for (VMFlag flag : flags.values()) {
 601             printConfigLine(vm, "[vmconfig:flag] %s %s = %s%n", flag.type, flag.name, flag.value);
 602         }
 603         TreeMap<String, Long> addresses = new TreeMap<>(store.getAddresses());
 604         for (Map.Entry<String, Long> e : addresses.entrySet()) {
 605             printConfigLine(vm, "[vmconfig:address] %s = %d[0x%x]%n", e.getKey(), e.getValue(), e.getValue());
 606         }
 607         TreeMap<String, Long> constants = new TreeMap<>(store.getConstants());
 608         for (Map.Entry<String, Long> e : constants.entrySet()) {
 609             printConfigLine(vm, "[vmconfig:constant] %s = %d[0x%x]%n", e.getKey(), e.getValue(), e.getValue());
 610         }
 611         for (VMIntrinsicMethod e : store.getIntrinsics()) {
 612             printConfigLine(vm, "[vmconfig:intrinsic] %d = %s.%s %s%n", e.id, e.declaringClass, e.name, e.descriptor);
 613         }
 614     }
 615 
 616     /**
 617      * Gets an output stream that writes to HotSpot's {@code tty} stream.
 618      */
 619     public OutputStream getLogStream() {
 620         return new OutputStream() {
 621 
 622             @Override
 623             public void write(byte[] b, int off, int len) throws IOException {
 624                 if (b == null) {
 625                     throw new NullPointerException();
 626                 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) {
 627                     throw new IndexOutOfBoundsException();
 628                 } else if (len == 0) {
 629                     return;
 630                 }
 631                 compilerToVm.writeDebugOutput(b, off, len);
 632             }
 633 
 634             @Override
 635             public void write(int b) throws IOException {
 636                 write(new byte[]{(byte) b}, 0, 1);
 637             }
 638 
 639             @Override
 640             public void flush() throws IOException {
 641                 compilerToVm.flushDebugOutput();
 642             }
 643         };
 644     }
 645 
 646     /**
 647      * Collects the current values of all JVMCI benchmark counters, summed up over all threads.
 648      */
 649     public long[] collectCounters() {
 650         return compilerToVm.collectCounters();
 651     }
 652 
 653     /**
 654      * The offset from the origin of an array to the first element.
 655      *
 656      * @return the offset in bytes
 657      */
 658     @SuppressWarnings("static-method")
 659     public int getArrayBaseOffset(JavaKind kind) {
 660         switch (kind) {
 661             case Boolean:
 662                 return Unsafe.ARRAY_BOOLEAN_BASE_OFFSET;
 663             case Byte:
 664                 return Unsafe.ARRAY_BYTE_BASE_OFFSET;
 665             case Char:
 666                 return Unsafe.ARRAY_CHAR_BASE_OFFSET;
 667             case Short:
 668                 return Unsafe.ARRAY_SHORT_BASE_OFFSET;
 669             case Int:
 670                 return Unsafe.ARRAY_INT_BASE_OFFSET;
 671             case Long:
 672                 return Unsafe.ARRAY_LONG_BASE_OFFSET;
 673             case Float:
 674                 return Unsafe.ARRAY_FLOAT_BASE_OFFSET;
 675             case Double:
 676                 return Unsafe.ARRAY_DOUBLE_BASE_OFFSET;
 677             case Object:
 678                 return Unsafe.ARRAY_OBJECT_BASE_OFFSET;
 679             default:
 680                 throw new JVMCIError("%s", kind);
 681         }
 682 
 683     }
 684 
 685     /**
 686      * The scale used for the index when accessing elements of an array of this kind.
 687      *
 688      * @return the scale in order to convert the index into a byte offset
 689      */
 690     @SuppressWarnings("static-method")
 691     public int getArrayIndexScale(JavaKind kind) {
 692         switch (kind) {
 693             case Boolean:
 694                 return Unsafe.ARRAY_BOOLEAN_INDEX_SCALE;
 695             case Byte:
 696                 return Unsafe.ARRAY_BYTE_INDEX_SCALE;
 697             case Char:
 698                 return Unsafe.ARRAY_CHAR_INDEX_SCALE;
 699             case Short:
 700                 return Unsafe.ARRAY_SHORT_INDEX_SCALE;
 701             case Int:
 702                 return Unsafe.ARRAY_INT_INDEX_SCALE;
 703             case Long:
 704                 return Unsafe.ARRAY_LONG_INDEX_SCALE;
 705             case Float:
 706                 return Unsafe.ARRAY_FLOAT_INDEX_SCALE;
 707             case Double:
 708                 return Unsafe.ARRAY_DOUBLE_INDEX_SCALE;
 709             case Object:
 710                 return Unsafe.ARRAY_OBJECT_INDEX_SCALE;
 711             default:
 712                 throw new JVMCIError("%s", kind);
 713 
 714         }
 715     }
 716 
 717     /**
 718      * Links each native method in {@code clazz} to an implementation in the JVMCI SVM library.
 719      * <p>
 720      * A use case for this is a JVMCI compiler implementation that offers an API to Java code
 721      * executing in HotSpot to exercise functionality (mostly) in the JVMCI SVM library. For
 722      * example:
 723      *
 724      * <pre>
 725      * package com.jcompile;
 726      *
 727      * import java.lang.reflect.Method;
 728      *
 729      * public static class JCompile {
 730      *     static {
 731      *         HotSpotJVMCIRuntime.runtime().registerNativeMethods(JCompile.class);
 732      *     }
 733      *     public static boolean compile(Method method, String[] options) {
 734      *         // Convert to simpler data types for passing/serializing across native interface
 735      *         long metaspaceMethodHandle = getHandle(method);
 736      *         char[] opts = convertToCharArray(options);
 737      *         return compile(metaspaceMethodHandle, opts);
 738      *     }
 739      *     private static native boolean compile0(long metaspaceMethodHandle, char[] options);
 740      *
 741      *     private static long getHandle(Method method) { ... }
 742      *     private static char[] convertToCharArray(String[] a) { ... }
 743      * }
 744      * </pre>
 745      *
 746      * The implementation of the native {@code JCompile.compile0} method would be in the SVM library
 747      * that contains the bulk of the JVMCI compiler. The {@code JCompile.compile0} implementation
 748      * will be exported as the following JNI-compliant symbol:
 749      *
 750      * <pre>
 751      * Java_com_jcompile_JCompile_compile0
 752      * </pre>
 753      *
 754      * How the JVMCI compiler SVM library is built is outside the scope of this document.
 755      *
 756      * @see "https://docs.oracle.com/javase/10/docs/specs/jni/design.html#resolving-native-method-names"
 757      *
 758      * @throws NullPointerException if {@code clazz == null}
 759      * @throws IllegalArgumentException if the current execution context is SVM or if {@code clazz}
 760      *             is {@link Class#isPrimitive()}
 761      * @throws UnsatisfiedLinkError if the JVMCI SVM library is not available, a native method in
 762      *             {@code clazz} is already linked or the SVM JVMCI library does not contain a
 763      *             JNI-compliant symbol for a native method in {@code clazz}
 764      */
 765     @SuppressWarnings({"static-method", "unused"})
 766     public void registerNativeMethods(Class<?> clazz) {
 767         throw new UnsatisfiedLinkError("SVM library is not available");
 768     }
 769 }